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のコードに見えないし、オブジェクトにメッセージを送るというか、メソッドを後ろにつなげていくという流れだったのが、何だか逆流してる感じがする。ブラケットを使っていると、インデックスアクセスみたいだし、何とも不自然。