さっき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で変更してあげて解決。

こう書くと楽勝っぽいけど、ほんと時間を使った。かなりググったのでautopagerizeldrizeがめちゃくちゃ活躍した。もしかして、phpな人には恥ずかしいような内容なのかもしれないが、書いておく。

D

これを見てVimperator入れた。
でも、viよりemacsが好きなのでemacsキーバインド風にした。

.vimperatorrc

map                                                                                                                                                  
map                                                                                                                                                  
map                                                                                                                                                  
map                                                                                                                                                  
map                                                                                                                                                  
map  gT                                                                                                                                                   
map  gt                                                                                                                                                   
map  j                                                                                                                                                    
map  k                                                                                                                                                   
map  

とかそんな感じで書いた。
viewの部分の設定を無効にしたfiremacsと併用して超快適。

外部イテレータについてメモ。

require 'generator'

g = Generator.new([1,2,3])

while g.next?
  puts g.next
end

ってやれば、Rubyで外部イテレータが使える。

で、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 でもいいのかなーと思ってためしたらだめだった。

*1:これでいうと[1,2,3]

*2:手元のMacBookでは1000回回すだけで2秒くらいかかった

*3:Route 477(2007-12-15)より

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

ってな感じで期待通り。
マニュアルはちゃんと読もうと思った。