イテレータ変換

内部イテレータを外部イテレータに変換します。

  • あ、えーと、一言で言えば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"