背景
rubyで遅延評価のため、ブロック付きメソッド(内部でyieldを呼ぶメソッド)を作ることがある。
例えばこんなの
def aaa(list) do
list.each do |x|
yield x
end
end
遅延評価のときは、
aaa([1,2,3]) do |x|
puts x
end
とすればよいだろう。
ところが、Arrayとしてまとめて評価したい時がある。特にテストの時、
a = []
aaa([1,2,3]) do |x|
a << x
end
assert_equal([1,2,3], a)
みたいに書かないといけない。できれば、
a = aaa([1,2,3])
assert_equal([1,2,3], a)
とできないか、ということ。
Enumeratorを使う
Enumeratorを使うと、
例えば
def aaa(list)
Enumerator.new do |a|
list.each do |x|
a << x
end
end
end
とできる。ただしこの関数はブロックを引数とすることができるが、yieldは隠れてしまい、
Enumeratorクラスの事前知識がなければ、コードを見ただけではブロックを引数とすることはわからないかもしれない。
このEnumeratorクラスを使ったメソッドの遅延評価をしたいときは、
aaa([1,2,3]).each do |x|
puts x
end
となり、Arrayを取得したいときは、
irb> aaa([1,2,3]).to_a
=> [1, 2, 3]
と、rubyを使う人にとっては、自然な実装ができる。
ブロックを書く
あるいは、次のようなシンプルなブロックをつけて、to_aメソッドでチェインしてやると、
irb> aaa([1,2,3]){|x|x}.to_a
=> [1, 2, 3]
とArrayが得られる。このブロックはもっとシンプルに、
{}
と表すことが出来る。すなわち、
irb> aaa([1,2,3]){}.to_a
=> [1, 2, 3]
となる。ぱっと見、コーディングミスのようにも見える。
このシンプルなブロックを書く(見る)のが気持ち悪い、という場合は、
次のように変数(オブジェクト)化して隠すこともできる。
irb> b = lambda{|x|x}
irb> aaa([1,2,3], &b).to_a
=> [1, 2, 3]
ただし上記のように、その代わりブロック変数の引数が見えてしまう…。
まとめ
ブロック付きメソッドの出力をArrayにしたい場合は、メソッドをEnumeratorクラスを利用して作成するか、シンプルなブロックとして与えて、to_aメソッドをチェインする。