[python] switchもどき

ipythonにはswitch, case文はありません。
breakの書き忘れで変なバグを生まないように…だとか。
でも、if, elifを重ねるのって嫌ですよね。
特に同一変数の評価をif, elifで書くとなんだか非効率な気もする…

そこで、こんな書き方。

n = 0
x = 100
val = {
 0: lambda x: x + 1,
 1: lambda x: x + 2,
 2: lambda x: x + 3
}.get(n)(x)
print val # 101

ちょっとわかりにくい気もしますが、
イディオムとして覚えてしまえば、switch, caseとさほど変わらないです♪


[python] difflibを使ってみる

pythonには、標準でdifflibというモジュールが含まれています。

いわゆる、linuxのdiff相当のことができます。

import difflib
a = '''
abc
def
ghi
'''
b = '''
abc
ddf
ghi
'''
list_a = a.splitlines()
list_b = b.splitlines()
print list_a  # ['', 'abc', 'def', 'ghi' ]
print list_b # ['', 'abc', 'ddf', 'ghi' ]
for line in difflib.unified_diff(a, b):
      print line

result:
@@ -1,4 +1,4 @@
 abc
-def
+ddf
 ghi

diff -uと同じような出力を得られます。

[Java]Java7からはヨーダ記法はイディオムではなくなった?

ヨーダ記法というのがある。オブジェクト(特に文字列)を比較する際にnullチェックを省略するために対象のオブジェクトではなく、リテラルの方のequalsメソッドを使う手法だ。

String jedi;
...
if ("Yoda".equals(jedi)) 
...
確かにこれならstrがnullでもNullPointerExceptionが発生しないし、条件分岐はfalse側に行く。

なんだけど、Java7からはObjectsクラスが導入された。
Objectsメソッドにはstaticメソッドとしてequlasメソッドが存在するので、こちらを使うほうが今後のイディオムになるかもしれない。

boolean Objects::equals(Object a, Object b)

このメソッドはaとbのどちらかがnullの場合はfalseを返し、両方がnullの場合はtrueを返す。両方ともnullではない場合はa.equals(b)の結果が返される。
Objectsメソッドにはequalsメソッド以外にもオブジェクト比較やnullチェックに便利なメソッドがあるので、Java7以降でコードを書く場合は使ってみるといいかもしれない。

ちなみになぜヨーダ記法なんて呼ばれているかというと。。。
スターウォーズのヨーダが通常の英語とは違う語順でしゃべるからだそうです。

[シェルスクリプト]ランダムな数値

シェルスクリプトでランダムな数値を得る必要があったのでちょっと調べてみました。

bashならRANDOM変数が使える。
例えば0から9の整数をランダムに得たければ、以下のようにする。

$ echo $(($RANDOM % 10))

$RANDOMは0から32767までの整数を返すから、お手軽にやりたければこれでもいい。
だけど、bash以外のシェルを使う場合や、乱数を複数得たい場合は/dev/urandomを使うほうがよさそう。
試しに以下を実行すると画面にバイナリのデータが延々と表示される。

$ cat /dev/urandom

Linuxの/dev/urandomを読み込むと擬似乱数ビット列が生成されるため、このような表示になる。
これを数字文字列に変えるにはodコマンドを使う。

$ od -vAn --width=4 -tu4 </dev/urandom

-vAnは出力表示のオプション。
-vを指定しないと直前の行と同じデータはアスタリスクで出力されてしまう。
-Anは入力データのオフセットの表示を抑止する。

--width=4も出力表示のオプションだけど、1行が何バイトかを指定する。
※なぜか短縮形式の-wは推奨されないと書いてある。
http://linuxjm.osdn.jp/html/gnumaniak/man1/od.1.html

-tu4は出力フォーマットを指定している。この場合は符号なし4バイト整数を指定している。

ただし、このままでは延々と数字が表示され続けるので、-Nオプションで何バイトで終了するかを指定する。

$ od -vAn --width=4 -tu4 -N40 </dev/urandom

この場合4x10で10行出力された時点で終了する。
もちろん1行だけ必要な場合は-N4とする。

$ od -vAn --width=4 -tu4 -N4 </dev/urandom

これを0から9等に制限したければ、以下のようにawkで処理すればいい。

$ od -vAn --width=4 -tu4 -N4 </dev/urandom | awk '{ print $1 % 10 }'

参考Webサイト
シェルやシェルスクリプトで乱数を使う2つの方法

[JavaScript] 文字列結合の速さ

どんな言語でもよく使う文字列結合。

+演算子を使って結合する方法と、配列に入れてからjoinする方法
どちらが速いのか実験してみました。
var n = 1000000
  , s = 'abc'
  , str = ''
  , arr = []
  , arr2 = []
  , i;

console.log('試行回数 :' + n);

console.time(' + ');
for (i = 0; i < n; i++) {
 // +演算子で文字列結合
  str += s;
}
console.timeEnd(' + ');

console.time(' join ');
for (i = 0; i < n; i++) {
  // 配列に追加(push)
  arr.push(s);
}
// 文字列化
arr.join('');
console.timeEnd(' join ');

console.time(' index ');
for (i = 0; i < n; i++) {
  // 配列に追加(index指定)
  arr[i] = s;
}
// 文字列化
arr.join('');
console.timeEnd(' index ');
結果
(n=1000000)の場合FireFox(38)Chrome(43)IE(11)
+演算子7~140ms80ms400ms
push/join30ms230ms450~500ms
index/join22ms40ms280ms

(n=10)の場合FireFox(38)Chrome(43)IE(11)
+演算子0.40ms0.10ms0.10~0.15ms
push/join0.30ms0.05ms0.10~0.15ms
index/join0.20ms0.03ms0.10ms

どの値も、実行するたびに上下しますが大体こんな感じでした。
この結果を見ると、joinを使ったほうが速そうです。
ただ、pushを使っているとブラウザによっては時間がかかるようなので
index指定を使ったほうが安心ですね。

[JavaScript]整数の最大値

JavaScriptで整数をインクリメントしていくと、あるところでそれ以上大きくならなくなる。

for (let i = 0; i < veryLargeNumber; i++) {
     ...
}
...
9007199254740989
9007199254740990
9007199254740991
9007199254740992
9007199254740992
9007199254740992
9007199254740992
...


9007199254740992をインクリメントしても9007199254740992になってしまう。
JavaScriptのNumber型は64bitの倍精度浮動小数点型で表現されるので、整数を正確に表せるのは仮数部の53bit分ということになる。

16進数で書くとこんな。
0x1FFFFFFFFFFFFF
9007199254740991(10進)

だから、9007199254740991を超えると整数の値が不正確なものになってしまう。
試しに以下を実行して、10進表現と2進表現を表示すると

for (let i = 0; i < 10; i++) {
    print(0x1FFFFFFFFFFFFF + i + ': ' + (0x1FFFFFFFFFFFFF + i).toString(2));
}


9007199254740991: 11111111111111111111111111111111111111111111111111111
9007199254740992: 100000000000000000000000000000000000000000000000000000
9007199254740992: 100000000000000000000000000000000000000000000000000000
9007199254740994: 100000000000000000000000000000000000000000000000000010
9007199254740996: 100000000000000000000000000000000000000000000000000100
9007199254740996: 100000000000000000000000000000000000000000000000000100
9007199254740996: 100000000000000000000000000000000000000000000000000100
9007199254740998: 100000000000000000000000000000000000000000000000000110
9007199254741000: 100000000000000000000000000000000000000000000000001000
9007199254741000: 100000000000000000000000000000000000000000000000001000



9007199254740992が2回現れたり、9007199254740996が3回現れたりするけど、9007199254740993や9007199254740995というのは現れない。
これは仮数部の端数処理が2進数の再近接偶数丸めで行われているからだ。端数は最も近い表現可能な数に丸められるけど、上下の表現可能な数から等距離の場合、丸め後の値が偶数になるように丸められる。
その結果、以下の矢印の方向にまるめられ、9007199254740992が2回出現し、9007199254740996が3回出現することになる。

9007199254740992: 10000000000000000000000000000000000000000000000000000 0
9007199254740993: 10000000000000000000000000000000000000000000000000000 1↓
9007199254740994: 10000000000000000000000000000000000000000000000000001 0
9007199254740995: 10000000000000000000000000000000000000000000000000001 1↑
9007199254740996: 10000000000000000000000000000000000000000000000000010 0
9007199254740997: 10000000000000000000000000000000000000000000000000010 1↓
9007199254740998: 10000000000000000000000000000000000000000000000000011 0

[python] SyntaxError. Non-ASCII character

タイトルのエラーが出たら、
文字のエンコードの問題。

ファイルの先頭に
# coding: utf-8
と書くと回避できる。
coding:のgと:の間に空白があるとダメ。:とutf-8のuの間は空いてもオッケー。

界隈では、-*- coding: utf-8 -*-なんて書き方をよく見かける。emacs流らしい。

[python] 辞書のループ

いつも忘れるので、メモっておこう

dic = { 'name': 'Mizu.A',  'age': 33 }
for k, v in dic.iteritems():
   print k + '=' + str(v)

result:
age = 33
name = Mizu.A

JavaScriptのループ その3

[その1]では以下のようなループで変数iのスコープで関数全体になってしまうと書いた。
これはJavaScriptでは変数にブロックスコープがなく、var変数はどこで宣言しても関数の先頭で宣言したのと同じように扱われてしまうからだ。
※ちなみに変数宣言が関数の先頭に移動してしまう挙動をホイスティング(巻き上げ)と呼ぶ。
for (var i = 0; i < 5; i++) {
    ...
}
print(i);
出力結果は以下となる。

5

ECMAScript6をサポートしている環境ではvarではなくletキーワードが使える。
for (let i = 0; i < 5; i++) {
   ...
}
print(i);
let変数はブロック変数となるため、出力結果は以下となる。

ReferenceError: i is not defined