オリジナルのIteratorを定義する
イテレータの使い方は何度も書いてきましたが、今回はその定義方法を書きます。
XtalのIteratorは次のメソッドを定義する必要があります。
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
RubyやPythonに比べ、なんかややこしいな、と思った方も多いでしょう。
試しにそれらの言語での例を書いてみます。
# 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が定義されているので、イテレータとして扱うことができるわけです。