Rubyでカリー化
ピクセル単位でRGB値を比較するCで書いた重たい関数、
doulbe diff(buf *b1, buf *b2)
というのを高速化するために、
doulbe diff(buf *b1, buf *b2, int step) : for (i = 0; i < height; i+=step) for (j = 0; j < width; j+=step) : : d = diff(b1, b2, 4); or d = diff(b1, b2, 2); or d = diff(b1, b2, 1);
という風にして、ループを適当に間引くことで高速化しようとした。ところが、ちょっと間引くだけで(予想に反して)精度がガタ落ちになったので、結局、stepとして渡す値は1か2ということになった。それならいっそ、
doulbe diff_step2(buf *b1, buf *b2) doulbe diff_step1(buf *b1, buf *b2)
と2つの関数にわけたほうが意図が明確になる。でも、それだけのために、中身がほとんど同じ関数を増やすのって何かバランス悪いし、この2つの関数を「diff」のラッパ関数として使うのも、何だかコードが増える割にご利益が少なくてバランスが悪い。
もしかしてカリー化って、こういうときに使うものじゃないかと思った。Rubyでカリーっぽいことをやってみた。(Ruby1.9系の)Procクラスにはcurryメソッドというのがある。
>> step_index = ->(s,a) {0.step(a.size, s)}.curry => #<Proc:0x82d4ecc> >> hex = step_index[16] => #<Proc:0x8296348> >> oct = step_index[8] => #<Proc:0x8293f58> >> a = (1..100).to_a => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100] >> hex.(a) => #<Enumerator:0x8249c64> >> hex.(a).each{|i|puts i} 0 16 32 48 64 80 96 => 0 >> oct.(a).each{|i|puts i} 0 8 16 24 32 40 48 56 64 72 80 88 96 => 0 >>
うーん、何か単に名前を付ける程度の話だったかも。いや、括りだしたパターン(ステップnのイテレータオブジェクト)に再利用するメリットがあれば、もう少し意味があるのかな。
ところでEnumeratorオブジェクトの中を表示するのに、毎回each{|i|puts i}とかやるのがめんどくさい。each putsをepとかにして、
>> ep = ->(x){x.each{|i| puts i}} => #<Proc:0x8450bac@(irb):91 (lambda)> >> ep.(hex.(a)) 0 16 32 48 64 80 96 => 0 >> ep[hex[a]] 0 16 32 48 64 80 96 => 0
とかできるんだけど、とてもじゃないけど、Rubyのコードに見えないし、オブジェクトにメッセージを送るというか、メソッドを後ろにつなげていくという流れだったのが、何だか逆流してる感じがする。ブラケットを使っていると、インデックスアクセスみたいだし、何とも不自然。