イテレータ変換
- あ、えーと、一言で言えばeachをnextに変換するということです。
- each-ableなコレクションを渡してIteratorオブジェクトを作ります。
- callccを使います。
- 最後に達したときにはnextはnilを返し、自動的にrewindされるものとします。
class Iterator def initialize(eachable) @eachable = eachable @cc = nil end def next if @cc @cc.call(false) else @eachable.each do |e| return e if callcc { |@cc| true } end @cc = nil end end end it = Iterator.new([10, 20, 30]) while e = it.next p e end it = Iterator.new(DATA) while e = it.next p e end __END__ Alpha Beta Gamma
実行結果です。
10 20 30 "Alpha\n" "Beta\n" "Gamma\n"
まあ、なんて素敵♪
追記:「えっへん」と思って作ったのですが、もしかしたらこれはクラスライブラリにあるGeneratorの再発明?…ぐ、ぐすん。
- そっかー。戻り値ではなくnext?というテストメソッドを用意すればよかったのかー。
- each-ableというよりもEnumerableなのかー。ふみー。
……というわけで、逆変換です。nextからeachへの変換。これはとても簡単です。Generatorオブジェクトを作るときにすでにEnumerableオブジェクトを渡しているので、ナンセンスなのですけれどね。まあ、練習ということで♪
require 'generator' class Eachable def initialize(generator) @generator = generator end def each while @generator.next? yield(@generator.next) end end end Eachable.new(Generator.new([10, 20, 30])).each do |e| p e end Eachable.new(Generator.new(DATA)).each do |e| p e end __END__ Alpha Beta Gamma
実行結果です。
10 20 30 "Alpha\n" "Beta\n" "Gamma\n"