オリジナルのIteratorを定義する

イテレータの使い方は何度も書いてきましたが、今回はその定義方法を書きます。
XtalIteratorは次のメソッドを定義する必要があります。

5/19 iter_firstの意味を若干変更しました。

iter_next: method()
次の要素を指すイテレータと値を多値で返す。
このイテレータがミュータブルなら、次の要素を指すイテレータとして自身を返しても構わない。
もし要素が無い場合、nullを返す。

iter_first: method()
ブロック適用の際に一番最初に呼ばれるiter_next。
ブロック適用開始時にしたい特別な処理がなければiter_nextと同一でよい。

iter_break: method()
イテレータが途中で中断される場合に呼び出される。
これは必要なければ定義しなくてもかまわない。

では、配列の要素を逆順に取り出すイテレータの定義方法を例として示します。

ArrayReverseIterator: class(Iterator){

  @array;
  @index;

  initialize: method(array){
    @array = array;
    @index = @array.length;
  }

  iter_next: method(){
     if(@index==0){
       return null;
     }
     @index--;
     return this, @array[@index];
  }

  // iter_firstはiter_nextと同一にする
  iter_first: iter_next;
}

values: [0, 1, 2];
iter: ArrayReverseIterator(values);

iter{
  it.p;
}
// 2
// 1
// 0

RubyPythonに比べ、なんかややこしいな、と思った方も多いでしょう。
試しにそれらの言語での例を書いてみます。

# ruby
def reverse(array)
  n = array.length
  while n!=0
    n -= 1
    yield array[n]
  end
end

reverse([0, 1, 2]){ |it|
 puts it
}
# python
def reverse(array):
    i = len(array)
    while i!=0:
        yield array[i]

for it in reverse([0, 1, 2]): 
    print it

とてもイテレータ、ないしはジェネレータが書きやすいですね。
大丈夫、Xtalにもこのような簡単な記述でイテレータを作る方法があります。
それには fiber を使います。

reverse: fun(array){
  return fiber{
    i: array.length;
    while(i!=0){
      i--;
      yield array[i];
    }
  }
}

reverse([0, 1, 2]){
 it.p;
}

このとおり、Xtalも内部イテレータかのように記述できます。
fiberにはiter_first, iter_next, iter_breakが定義されているので、イテレータとして扱うことができるわけです。