iterator

XtalはまるでRubyのような書式で配列の要素を順次取り出すことが出来ます。例えば次のように。

 // 変数arrayを定義。初期値に配列を。
 array : [10, 20, 30, 40];
 array.each{ |value| 
   println(value);
 }

このようにぱっと見はRubyにしか見えません。しかし、動作はRubyとは異なります。


Rubyでは上の書き方を、「ブロック付きメソッド呼び出し」といいます。多分。その呼び名の通り、「eachメソッドにブロックを渡している」ようなイメージです。


Xtalはそうでなく、「eachメソッドが返したiteratorオブジェクトに対しブロックを適用している」
というイメージとなります。


Xtalはブロック適用を次のようにループ文として展開します。

 // イテレートを開始し、同時に最初の要素を得る
 // iter_firstは次のiteratorと最初の要素を返すメソッドです。
 _it, value : array.each.iter_first(); 
 try{
   while(_it){ // 
   
     println(value);

     // イテレータを次に進め、同時に次の要素を得る
     // iter_nextは次のiteratorと次の要素を返すメソッドです。
     _it, value = _it.iter_next();
   }
 }finally{
   // もしイテレータが終了していなくて、iter_breakメソッドが存在するなら、それを呼び出す
   if(_it)_it.?iter_break();

   // .? はそのメソッドが存在してるときだけ呼び出す構文です。 
 }

このため、Xtalにおいてブロックは完全に制御構造であり、値を返さない文です。


この違いのため、Rubyと同名のメソッドでも、若干動作が違うメソッドがあります。
例えば、selectメソッドです。
Rubyでは「selectメソッドは各要素に対してブロックを評価した値が真であった要素を全て含む配列を返します」
Xtalでは「selectメソッドは各要素に対して渡された関数を評価した値が真であった要素を反復するイテレータを返します」

Rubyを使っていた方はこの「ブロック適用は文である」を、大きな欠点に捉えるかもしれません。値を返さないのならば次のように書けないのですから。


# 配列の中の奇数だけ取り出して、それらを2倍にした値を,で連結して表示

 puts array.select{ |x| x%2 == 1 }.collect{ |x| x*2 }.join(", ")


Xtalでは上のを替わりに次のように書きます

 println(array.each.select(fun(x) x%2 == 1).collect(fun(x) x*2).join(", "));

見たとおり、selectやcollectはブロックをとるようになっていません。普通に関数オブジェクトを渡すようになってます。Xtalでは簡易的な関数式の記述が出来るので、さほどタイプは増えません。



このようなXtalの動作は、Rubyには無い利点が一応あります。Rubyではselectやcollectの度にその結果の配列が生成されますが、Xtalでは要素が一つづつ順順に流れて処理されるのです。


以下簡単な例。

Rubyで、配列の中の奇数だけ取り出して、一つづつ表示は次のように書けます

 array.select{ |x| x%2 == 1 }.each{ |x| puts x }

Xtalでは上のを次のように書きます

 array.each.select(fun(x) x%2 == 1){ |x| puts x; }

なぜeachが挟まるかというと、Arrayクラスにはeachしか定義されてなく、selectはIteratorモジュールのメソッドだからです。


Rubyで、配列の中の奇数だけ取り出して、それらを2倍にした値を,で連結して表示、は次のように書けます

 puts array.select{ |x| x%2 == 1 }.collect{ |x| x*2 }.join(", ")

Xtalでは上のを次のように書きます

 println(array.each.select(fun(x) x%2 == 1).collect(fun(x) x*2).join(", "));