■
さっきjavaのIntegerのtoStringを読んだ。で、その過程で、IntegerのgetChars(int i, int index, char[] buf)の部分の実装(の一部)で感動したので、それを書く。あんまりjavaの話ではない。
感動した部分はこれの、
// Generate two digits per iteration while (i >= 65536) { q = i / 100; // really: r = i - (q * 100); r = i - ((q << 6) + (q << 5) + (q << 2)); i = q; buf [--charPos] = DigitOnes[r]; buf [--charPos] = DigitTens[r]; }
ここ
r = i - ((q << 6) + (q << 5) + (q << 2));
コメントから推測するに、どうやら、
((q << 6) + (q << 5) + (q << 2))
で q * 100 が計算できているようなのだが、最初意味がわからなかった。
まず、ほんとにそうなってるのかとりあえずirbで確認してみる。
irb(main):001:0> i = 65536
=> 65536
irb(main):002:0> (i << 6) + (i << 5) + (i << 2)
=> 6553600
やはり、100倍になっているようだ。
で、しばらく考えて、1ビットずらすということは、2倍するのと同じだったよなーと思う。そこで元の数字の2の6乗 + 2の5乗 + 2の2乗を足してみる。
irb(main):001:0> i = 65536 => 65536 irb(main):002:0> (i * (2 ** 6)) + (i * (2 ** 5)) + (i * (2 ** 2)) => 6553600
やはり、これで100倍になってるみたい。
ここで、やっと気がついて、iでくくってみる。すると
irb(main):001:0> i = 65536 => 65536 irb(main):002:0> i * ((2 ** 6) + (2 ** 5) + (2 ** 2)) => 6553600
おお。同じだ。
たしかに64 + 32 + 4は100だ。
ということで、やっぱりビット演算だけで100倍してるみたい。すげー、っていうお話。
普通に100倍するよりもやっぱりはやいのかなー。
■
phpをいじらなければならない状況になった。ほぼはじめて。で、その際死ぬほどハマったことについて書く。
状況としてはあるファイル(たとえばlogin.php)で
$_SESSION[hoge] = 'foo';
とかやった値をべつのファイル(たとえばmain.php)で見ようとしてもどうしても見えないという感じだった。
他の人に聞くとこないだ動かしたときは無事に動いたとか言ってるから、プログラム自体には問題はなさそう。
で、まあ、なんのことはなく、session.save_pathに指定してあるディレクトリの所有者がおかしいだけだった。ということで、httpd.confをみて、chownで変更してあげて解決。
こう書くと楽勝っぽいけど、ほんと時間を使った。かなりググったのでautopagerizeとldrizeがめちゃくちゃ活躍した。もしかして、phpな人には恥ずかしいような内容なのかもしれないが、書いておく。
■
外部イテレータについてメモ。
require 'generator' g = Generator.new([1,2,3]) while g.next? puts g.next end
で、2つ以上をイテレートしたいときには、
require 'generator' s = SyncEnumerator.new([1,2,3], ['a', 'b', 'c']) s.each do |row| puts row end
ってやってあげればOK。どっちかが短い場合はそこにnilが入る。
これを外部イテレータなしでやるなら
[1,2,3].zip(['a','b','c']).each do |row| puts row end
とかやってあげればいいんだと思う。ただこの場合はzipされるほう*1が長くなきゃ最後まで行かない。
外部イテレータの方がきれいだけど、実行速度は遅いです*2。継続を使っているから。
以上、ほぼプログラミング言語 Ruby リファレンスマニュアルとプログラミング言語 Ruby リファレンスマニュアルからのネタでした。
ちなみに1.9なら
e = [1,2,3].each loop do puts e.next end
でOKです。要素がなくなったら nextがStopIterationを投げてloopがそれをうけとるらしい*3。
loopじゃなくてwhile true でもいいのかなーと思ってためしたらだめだった。
■
Hashについてなるほど、とおもったのでメモ。
初歩的すぎることっぽいけど自分がいままで知らなかったので書く。
というかほとんどリファレンスマニュアルの引用。
ハッシュの初期値に配列を使いたいときに、今までは
Hash.new([])
ってやってたんだけど、これだと
irb(main):001:0> h = Hash.new([]) => {} irb(main):002:0> h[0] << "a" => ["a"] irb(main):003:0> p h {} => nil
とかなっちゃっててハッシュとしてうまく使えてなかった。
なんでだろー、とか思いつつ、どうにか別な方法をつかうことでしのいできたんだけれども、ふとしたことからリファレンスマニュアルをみるとこんな記述が。以下プログラミング言語 Ruby リファレンスマニュアルより引用。
Hash.new()は同じを参照するので…。
Hash.new(val) は、ハッシュのデフォルトオブジェクトに val を設定します。値が設定されていないハッシュの参照はこのデフォルトオブジェクトを返しているだけです。
h = Hash.new()
h[0] << 0
h[1] << 1
p h #=> {}
p h.default #=> [0, 1]上記で、<< はデフォルトオブジェクトを破壊的に変更するだけで、h[0] などに影響を与えません。+= などの破壊的でないメソッドで再代入する必要が有ります。
h = Hash.new()
h[0] += [0]
h[1] += [1]
p h #=> {0=>[0], 1=>[1]}
p h.default #=> []
なるほどー。超納得。
ということでどうすりゃいいんだろう、と思ってさらにちゃんとみてみると、プログラミング言語 Ruby リファレンスマニュアルに
Hash.new {|hash, key| ...} (ruby 1.7 feature)
空の新しいハッシュを生成します。ifnone はキーに対応する値が存在しない時のデフォルト値です。デフォルト値の扱いには注意が必要です( trap::Hash )。
ruby 1.7 feature: ブロックを指定した場合は、ブロックの評価結果がデフォルト値になります。値が設定されていないハッシュ要素を参照するとその都度ブロックを実行し、その結果を返します。ブロックにはそのハッシュとハッシュを参照したときのキーが渡されます。
って書いてあったから、
Hash.new {|h,k| h[k] = []}
ってしてあげると解決。多分。
irbでも
irb(main):001:0> h = Hash.new{|h,k|h[k]=[]} => {} irb(main):002:0> h[0] = "a" => "a" irb(main):003:0> p h {0=>"a"} => nil
ってな感じで期待通り。
マニュアルはちゃんと読もうと思った。