タイトル

Synopsis 7:イテレータと怠惰

著者

Tim Nelson <wayland@wayland.id.au>
Daniel Ruoso <daniel@ruoso.com>
 

機種

Created: 27 Nov 2008

Last Modified: 2 Feb 2010
Version: 10
 

怠惰と意欲

我々がすべて知っている(とき・から・につれて・ように)、 Perl プログラマーの主要な美徳の1つが怠惰だ。 これはそれ自身同じく Perl の美徳の1つだ。 しかしながら、 Perl 6 は偽のだるさで死ぬほど愚かではなくて、そしてそれで時々熱心だ、そしていい加減な他のもの。

Perl が理解するという1つのことは怠惰と意欲の間の相違だ。 何かがいい加減であるとき、それは「ただ私にあなたが持っている;私が残り後に手に入れるであろうものをくれ」と言うのに対して、それが熱心であるとき、それは「もっと多くの ! を言う さらに多く! 私にあなたが受けとることができるすべてをくれ!」

Perl 6 は怠惰の4つのレベルを定義する:

厳密にいい加減だ

明示的に、いい加減でないオブジェクトを横断しないことを含んで、ユーザーによって必要とされないなら、何も評価しない。 この振る舞いは一般に、いい加減でないプリミティブと一緒に、ただプラグマによってあるいは露骨なプログラミングによって利用可能だ。

たいていいい加減だ

他のいい加減なオブジェクトの熱心な評価を起こさないで利用可能な項目を得ようとしろ。 しかしながら、実装は効率のためにバッチ処理をすることを許される。 実装がこのように前方に働いている、あるいは結果が不確定であるであろうとき、プログラマーは循環する副作用に頼ってはならない。 (しかしながら、これは純粋な definitional 堂々巡りを除外しない:もしリストが配列の名前にバインドされるなら、より以前の配列値が次の値の定義で使われるかもしれない。)

たいてい熱心だ

値の有限の数(たとえ正確な番号であるとしても前もって知られているではない)を表すために、(容易に)証明可能なすべての先行する条項と定義されて、すべての「容易な」項目を得ろ。 品目の残りが要求駆動型実行のために残っている。 同様知られていない構造が有限か、あるいは無限であるために、実装は許されて、しかしバッチ処理モードに前方にある程度の数の要素を努力して進むために、必要とされない。 もしこのようなバッチが終わることが分かるなら、リストは次の要素の熱心な評価でそうし続けるかもしれない。 「たいていいい加減な」ケースで、プログラマーが何でも循環する副作用の上に依存してはならない(とき・から・につれて・ように)実装が前方に機能することを可能にされる状態。 使用可能メモリのサイズに上がっていて、許された推測の評価の量に対する言葉定義限度がないことに注意を払え;しかしながら、実装が pragams の使用によって workahead の任意の限界を許すかもしれない。

ファイルからの I/O が一般に有限であると考えられるかもしれない、しかしソケットからの I/O がそうしないかもしれない。 どんなに、「有限である」の定義がいくぶん使用可能メモリに依存するから、あまりにも大きいファイルが合理的にであったとしても、いい加減に扱われろ。 何でも場合は、そこ(に・で)「たいていいい加減な」そして「たいてい熱心な」の間の深遠な意味の相違であるべきじゃない。 あなたがリストですべての値を使うことを望む可能性が高いかどうかについて、それらは実装に主にただヒントに過ぎない。 ゼロの transactional が差の上に依存することを許されるべきだ。

厳密に熱心だ

すぐにもしデータが構造化する何でも評価するように頼まれるなら無限であることが知られている適切なメッセージで失敗して、すべての項目を得ろ。 (効果的に無限である、しかし証明可能なようにそうじゃないデータ構造がその代わりに多分メモリを空にするだろう。) この振る舞いは一般に、ただプラグマによってあるいは明示的なプログラミング基本命令によって利用可能だ。

それぞれの演算の怠惰 / 意欲のレベルを決定することについての責任がそれぞれのいい加減なオブジェクトの外側であることを悟ることは重要だ; どの演算が実行されているかについて、ランタイムは依存して、適切レベルの怠惰を想定して、そしてそのレベルを適用する必要とされた演算を実行しようとしている。

若干の共通演算の怠惰レベル

リスト割り当て:私の @ 、 = @ 、何か;

リスト割り当てで p5 のような振る舞いを供給するために、もしあなたがそうするなら、この演算はそれを意味して、 Mostly Eager レベルで行なわれる

  my @a = grep { ... }, @b;
 

@ bが無限じゃない限り、 grep は評価されるだろう。

  my @a = grep { ... }, 1, 2, 3, 4..*
 

(たとえ最初の要素が知られているとしても)、 grep に無限のリストを与えるだろう、そのためにそれは同じくいい加減だろう。 他方

  my @a = grep { ... }, 1, 2, 3, 4;
 

熱心に評価されるだろう。

「たいてい熱心な」が若干のイテレータを潜在的に無限であるとして扱う、多数のターゲットへのリスト割り当てが(要求によって)十分なリストの評価に何でも埋めることを強いるにもかかわらず最終の「素晴らしい」目的地の前のスカラー目的地。 従って:

($a, $b, @c) = 1..*;
 

いい加減なイテレータが右側のそばに返して、そしてそれに漏らすことを強いる撮影1 そして2 準備する前に@c 平均に3..* いい加減に。

演算子:私の @ に < を与えろ== @ 何か;

飼料演算子は、ユーザーがどんな要素でも求める前に、演算が行なわれるべきじゃないことを意味して、厳密にいい加減だ。 それはどのようにかだ

  my @a <== grep { ... } <== map { ... } <== grep { ... } <== 1, 2, 3
 

たとえ1,2,3がかなり小さい周知の圧縮リストであるとしても、完全にいい加減だ。

けれども意欲が、それを意味して、怠惰に優先されることに気付くことは重要だ

  my @a = grep { ... } <== map { ... } <== grep { ... } <== 1, 2, 3
 

Will は熱心に評価される、しかしそれはまだ異なっているから

  my @d = 1,2,3;
  my @c = grep { ... }, @d;
  my @b = map { ... }, @c;
  my @a = grep { ... }, @b;
 

なぜなら第1で、処理は、第2の例が作る中間の熱烈なリストの創造を避けて、流れとして作られるだろうから。 他方

  my @d <== 1,2,3;
  my @c <== grep { ... }, @d;
  my @b <== map { ... }, @c;
  my @a = grep { ... }, @b;
 

最初の例の同じ怠惰レベルを提供する。

リストロール

List ロールが、完全に歩いて、リストへのいい加減なアクセスを表す:

データ構造(リスト、ツリー、表など)
送信(マップ、 grep など)
(主として IO のために)流れ出ろ
実際、リストのように振る舞うことを望むどんな値でも

リストのイテレータがリスト自身であることを悟ることは重要だ; Perl は2つのコンセプトを区別しない。 それがサポートする Any 型List ロールはイテレータだ。

このロールでのメソッドはそうだ:

メソッドが {...}得る

フラットなリスト(素晴らしいバインディングによって供給されたコンテキスト)の上に反復するために使われる。 イテレータから後で何でも平らにしている次の要素を先行する区画リターンする。

イテレータが要素を使い果たす時は、それは失敗するだろうEMPTY 例外。 たいていの失敗と同じように、EMPTY 一般に、「死者を出した使用」としての、呼び出しているレキシカルスコープがそれにスローされるように要請しない限り、それをスローすることなしで返送されている。 リストが今までにそうするべきじゃないを含む段落EMPTY その値が返されるとき、例外が、 iterational 制御流れから常に終わるべきだ。

メソッドバッチ($max ?){・・・}

あなたが一度に要素の任意の数を返すことを望むとき、フラットなリストの上に反復するために使われる。 要素の戻って来たセットは何も含んでいないことを保証されるParcel オブジェクト、何からでもそんなものParcel その要素を返す前に、平らにされるだろう。

適切な若干数で要素のバッチを返すPositional それが項目の正確な番号に数値化する型が戻った。 (型は同じくそうであるかもしれないIterable そのターンで。) 同じく戻るEMPTY ない場合引数は利用可能だ;EMPTY もしそれが切望されるなら、数の終わりテストを促進するために数値化されるとき、例外が0を返すべきだ。 もし$max ではないが供給されるという状態で、イテレータは適切なバッチのサイズを選ぶかもしれない。

このメソッドはイテレータにリストコンテキストを供給する。

メソッド getarg {・・・}

何も平らにならないで、イテレータから次の区画あるいは他のオブジェクトを返す。 それは、スライスコンテキストで variadically にスライス要素を検索することに加えて、共にポジショナルパラメータへのバインディングのために項目コンテキストで使われる。

それがオブジェクトを使い果たす時は、それは失敗するだろうEMPTY 例外。 たいていの失敗で、呼び出している(人たち・もの)じゃないなら、レキシカルスコープがそれにスローされるように要請することが一般にそれをスローしないで返送されている(とき・から・につれて・ように)「死者を出した使用」としてうか。 リストが今までにそうするべきじゃないを含む段落EMPTY その値が返されるとき、例外が、 iterational 制御流れから常に終わるべきだ。

注意しろ:get そしてgetarg 多分その通信がパスしているときからの1つのスレッドがそれらに関して実装される以上にイテレータが分け合った何のためにでも原子に違いない。

メソッド batcharg ($max ?){・・・}

適切な若干数で1回分の区画 / オブジェクトを返すPositional それが項目の正確な番号に数値化する型が戻った。 (型は同じくそうであるかもしれないIterable そのターンで。) 同じく戻るEMPTY ない場合引数は利用可能だ;EMPTY もしそれが切望されるなら、数の終わりテストを促進するために数値化されるとき、例外が0を返すべきだ。 もし$max ではないが供給されるという状態で、イテレータは適切なバッチのサイズを選ぶかもしれない。

メソッド平面{・・・}

これは通り過ぎて常に平らになるイテレータを呼び出すことリターンする.get 内部的に(どちらが、ただ区画の要素だけを返して、区画構造を捨てるか)。 あなたがコールするかどうかにかかわらず、返送されたイテレータは常に同じ値を返すだろう.get あるいは.getarg.

メソッドスライス{・・・}

これは通り過ぎて常に anti-flattensするイテレータを呼び出すことリターンする.getarg 内部的に、それから結果として生じている何でも方向転換によってそれを分ける隠ぺいSeq 外部からそれを返す前に。 A 傾けParcel それによってリストに変換されのSeq.  あなたがコールするかどうかにかかわらず、返送されたイテレータは常に同じ値を返すだろう.get あるいは.getarg.

List::PushBack ロール

それらが決して消費されないかのように、このロールは再び消費されるために後方にどのように値を受け取るべきか知っているイテレータを定義する。 イテレータは最初に、そして正しい順に消費されて、このロールではないからオリジナルのデータを修正して、ただそのデータの走査を修正するようにことだけをする意図されなかった値を断ることが自由だ。

メソッド pushback ($value){}

これは配列に戻っている $value を推し進める。 もしこのイテレータが不変のデータを proxying しているなら、オリジナルのオーダーからあるいはもしそれが消費されなかったならである値を断ることが自由だ。 このような場合、それは UnorderedPushBackException あるいは BadPushBackException で失敗するべきだ。

List::Unshift ロール

このロールはスタックの最上部で新しい要素を受け取ることができるイテレータを定義する、それは pushback のように機能する、しかしそれはこのストリームに完全に新しいオブジェクトを記憶することが可能であるべきだ。 変更可能な一覧表によって支援されることによって、あるいは、ローカルな山を供給することによって。 このロールは List::PushBack を暗示する、そしてそこで pushback が unshift によって扱われるだろう。

2つの異なったロールを定義することについての目的はユーザーに「失敗することを期待しろ」、あるいは「成功することを期待しろ」意味規則を与えるはずだ。

メソッド unshift ($value){}

支持されたリストへあるいはイテレータのローカルなスタックへのこの unshifts $value 。

補助員実装

Perl のビルトインは多くの補助員型が Lists を実装することを要求する。 これらは一般的な使用のために利用可能であって、そして、スカラー、配列、あるいは薄切りの配列において送信を終わらせることによって、インスタンスを作られる。

一般的な項目イテレータ

一般的な項目イテレータを作成するために、スカラーで送信を終わらせろ:

  my $it <== @a;
 

権限:後に、しろ:

  my $item = $it.get;
  my @items = @$it;
 

(30 TODO : $ の型制約についてそれをもたらせ - 多分平らになることを管理するために?)

(XXX TODO :

   What's this do?

   my $it <== @a; my $it2 <== $it; my $it3 <== $it; get $it2; get $it3;

   ...allow tag team suckling?

   Is it worth the dwim, or do we just forbid it?
 

一般的ないい加減なリスト

一般的ないい加減なリストを入手して、中に放送を終わらせることPositional.

my @it <== 1..2000;
 

一般的ないい加減なリストは要請に即座に対応する放送を消費する。 リストの要素がアクセスされる(とき・から・につれて・ように)、その後それぞれにあった値が戻ったCapture 置かれた指定期間の末であるPositional.  もしCapture 1つの値が返されるよりいっそう多くと一緒に、それぞれの値がそれ自身のインデックスにおいて記憶される。 Empty Captures 効果を持つな、そしてそれでそれらがまったく今までに存在したという情報は失われる。

権限:後にことをしろ:

@c = @it[1..5];
$s = @it[4];
 

...とこれは何でも見いだすイテレータにただいい加減に存在している値の負担を強いるかもしれない。 あなたがそうすることを望むかもしれないほどもしあなたが大きいシーケンスの分野で働いているなら、すでにイテレータから検索された値が解任されて、それらが存在しないなら、利用可能に保たれる:

shift(@it);
 

読むことと、一般的ないい加減なリストイテレータにインデックスを書くことはアクセスされた(の・もの・人)までそして含めてすべてのインデックスで消費と値をキャッシュすることを強制するだろう。

@it[4] = 2;  # @it[0..3] are cached now, @it[4] was consumed/destroyed
 

リストが始まることに付け加えることはただイテレータに触れないで値を記憶する。

@it.unshift(<a b c>);  # works as expected
 

何にでも要素の数を見いだそうとすることはずっとイテレータで値の残りの熱烈な消費を強制するかもしれない、そしてリストの終端にアクセスしようとすることは確かにそうするだろう。

@it.pop; # Eagerness happens.  Maybe bad things, too.
 

・・・、しかしあなたはリストが有限であることを念入りに確認する方が良い。 無限のリストの終わりをいじくる試みがまったく例外を上げているインタプリター、それがただ届けるかもしれない最も幸運なケースをもたらすかもしれないInfしかしあなたがあなた自身が庭の周りにラップをして非常に、非常に長い時間を過ごしているのを見いだすかもしれないという若干の場合で。 正確な振る舞いは実装に特定されているかもしれない。

それでそれをするな。

もしリストが有限であることが知られているなら、送信が消耗している途端に、一般的な配列イテレータは標準の配列に逆戻りするだろう。 それでそうすることは正当だ:

@it.push; # eagerly exhausts the feed, then turns into to a normal array
 

・・・同じ警告で無限について。

(30 TODO : @ の型の制約 / 形についてそれをもたらす?)

(XXX TODO :

   my @a = (1,2); @a <<== ... promotes an occupied Positional to
   iterator, right? Should probably be explicitly mentioned.
)



 

ノーブランドのいい加減なスライス

いい加減なスライスが消費するノーブランドParcel最初の次元が繰り返しと第2に対応する2次元のリストが値を含んでいるように、けれどもイテレータからのsが結果を記憶するParcel その繰り返しのために返されて、しかし回す中にSeq オブジェクト。 Empty Parcel オブジェクトが空きに変えられるSeq オブジェクト。 Any の他のオブジェクトがそれ自身として返される。

ノーブランドのいい加減なスライスを得るために、薄切りのバインディングで放送を終わらせろ。

my **@it <== map { ... }, 1,2,3;
 

それほど中に注意しろ:

my **@it <== (1,mysub(),2; 1..*);
 

スライスリストは独立していい加減だ。 しかしながら、そうするための呼び出しmysub() 特にいい加減なではない;区画が作成されるとき、それは起こる。 Any 怠惰が、呼び出しではなく、関数の帰りの値にある。 区画がそうである何ものトップのレベルでの呼び出しが熱心に呼び出される。 いい加減に関数を呼び出すために、呼び出しを何か他の明示的にいい加減な演算子に埋めることは必要だ:

1, (() ... { START mysub() }), 2  # harder way
1, lazy { mysub() }, 2            # easier way
 

[注意しろ:上記のテキストは他のどこか(に・で)本当に属する、しかし私はどこ(で・に)か理解するにはあまりにもいい加減だ。]

Coroutines

Perl6 が公式に定義された sub 風のコルーチンを持っていない。 疑いもなく異なった味にそうする外部モジュールがあるだろう。 マルチメソッド - ディスパッチと両立できて毎回同じ sub 、本当にではないに届く期待している異なったパラメータを持っている sub の名前にこのような構造、多くの呼び出しがどこを作ったか。 それがただ Perl6 言語のサブセットを使う若干のプログラミングスタイルに適しているかもしれない間に、それは Perl6 特徴セットの向こう側に一般に適用可能にされることができない。

このではないがあなたが Perl6 でコルーチンをすることができないと言う。 実際、ギャザー / 撮影構造は単純なコルーチンだ。 けれどももしあなたがそうすることを望むなら、と前後に Lua 風の値がパスする、あなたは suplimentary オブジェクトを使わなければならない:

 sub my_coro (*@slurp) {
    gather do {
        my Int $initval;
        my Str $secondval;
        my Int $thirdval;
        $initval = shift(@slurp);
        $initval++;
        take $initval;
        $secondval = shift(@slurp);
        take "$initval $secondval";
        $thirdval = shift(@slurp);
        take $thirdval + $initval;
    }
}
my @a = (1);
my $it;
$it <== my_coro() <== while 1 { shift(@a) };
say "First result: " ~ get $it;
@a.push("Hello World");
say "Second result: " ~ get $it;
@a.push(500);
say "Third result: " ~ get $it;
 

...もしあなたがパスすることを望む、それぞれの呼び出しに、あなたがそうすることができる多数のパラメータが、その代わりに、素晴らしいスライスをパスするために使うならCapture.

イテレータと配列はいっそう自然な感触を与えるためにもちろん着込ませられることができる:

class my_sub2coro {
    has $!it = Failure;
    has @!data = ();
    has $.coro;
    submethod BUILD {
        $!it <== &($.coro)() <== while 1 { shift(@!data) };
    }
    method corocall($message) {
        @!data.push($message);
        get $!it;
    }
}
my $c = my_sub2coro.new(coro => &my_coro);
say "First result" ~ $c.corocall(1);
say "Second result" ~ $c.corocall("Hello World");
say "Third result" ~ $c.corocall(500);
 

(30 TODO :放送がまだ実装されない(とき・から・につれて・ように)、上記の例コードがテストをしなかった)

付加

どうか perl6 - 言語にエラーとフィードバックをポストしてくれ。 もしあなたが一般的なリストを作っているなら、どうかトピックによるメッセージを分離してくれ。