11

Perlで遅延評価されたリストの良い解決策を見つけた人はいますか?私は次のようなものを変えるためにいくつかの方法を試しました

for my $item ( map { ... } @list ) { 
}

遅延評価に-たとえば、@listを結び付けることによって。コードをデバッグする能力を台無しにするので、私はそれを行うために分解してソースフィルターを書くことを避けようとしています。誰かが成功しましたか。それとも、分解してwhileループを使用する必要がありますか?

注:リストを機能的に変換するために、時々長いgrep-mapチェーンに夢中になっていることを言及する必要があると思います。つまり、foreachループやwhileループではありません。マップ式は、同じ垂直方向のスペースにより多くの機能を詰め込む傾向があるということです。

4

8 に答える 8

13

前述のように、for(each)は熱心なループであるため、開始する前にリスト全体を評価する必要があります。

簡単にするために、遅延評価された配列を作成するのではなく、イテレーターオブジェクトまたはクロージャーを使用することをお勧めします。ネクタイを使用して遅延評価された無限リストを作成することはできますが、リスト全体(またはリスト全体のサイズ)を(上記のforeachのように直接的または間接的に)要求すると、問題が発生する可能性があります。

フルクラスを記述したり、モジュールを使用したりすることなく、クロージャを使用するだけで単純なイテレータファクトリを作成できます。

sub make_iterator {
    my ($value, $max, $step) = @_;

    return sub {
        return if $value > $max;    # Return undef when we overflow max.

        my $current = $value;
        $value += $step;            # Increment value for next call.
        return $current;            # Return current iterator value.
    };
}

そしてそれを使用するには:

# All the even numbers between 0 -  100.
my $evens = make_iterator(0, 100, 2);

while (defined( my $x = $evens->() ) ) {
    print "$x\n";
}

CPANにはTie::Array :: Lazyモジュールもあります。これは、レイジーアレイへのはるかに豊富で充実したインターフェイスを提供します。私は自分でモジュールを使用したことがないので、マイレージは異なる場合があります。

ではごきげんよう、

ポール

于 2008-09-21T01:16:22.580 に答える
9

[補足: map/grep チェーンに沿った個々のステップは熱心であることに注意してください。一度に大きなリストを与えると、最終的な問題よりもはるかに早く問題が発生しますforeach。]

完全な書き直しを避けるためにできることは、ループを外側のループでラップすることです。これを書く代わりに:

for my $item ( map { ... } grep { ... } map { ... } @list ) { ... }

… 次のように書きます。

while ( my $input = calculcate_next_element() ) {
    for my $item ( map { ... } grep { ... } map { ... } $input ) { ... }
}

これにより、既存のコードを大幅に書き直す必要がなくなり、変換中にリストが数桁大きくならない限り、イテレータ スタイルへの書き直しが提供するほぼすべての利点が得られます。

于 2008-09-21T17:45:11.907 に答える
7

遅延リストを作成したい場合は、独自のイテレータを作成する必要があります。それができたら、Object::Iterateのようなイテレータ対応バージョンのmapとを使用できますgrep。そのモジュールのソースを見てください。これは非常に単純で、独自の反復子対応サブルーチンを作成する方法がわかります。

幸運を、 :)

于 2008-09-21T18:30:18.757 に答える
5

リスト全体を一度に生成しないようにforとforeachが最適化されている特別なケースが少なくとも1つあります。そしてそれが範囲演算子です。したがって、次のように言うオプションがあります。

for my $i (0..$#list) {
  my $item = some_function($list[$i]);
  ...
}

これにより、配列を反復処理し、値の長いリストを事前に作成せずに、好きなように変換します。

mapステートメントが可変数の要素を返すようにしたい場合は、代わりにこれを行うことができます。

for my $i (0..$#array) {
  for my $item (some_function($array[$i])) {
    ...
  }
}

これよりも広範囲にわたる怠惰が必要な場合は、クロージャを使用して怠惰なリストを生成する方法を学ぶのが最善の選択肢です。MJDの優れた本HigherOrderPerl、これらのテクニックを紹介します。ただし、コードにはるかに大きな変更が加えられることに注意してください。

于 2008-09-21T01:09:58.787 に答える
4

これを死からよみがえらせて、ポスターが探していたものを正確に実行List::GenするCPANのモジュールを作成したことを言及します。

use List::Gen;

for my $item ( @{gen { ... } \@list} ) {...}

リストの計算はすべて遅延処理であり、 map / grep に相当するものと他のいくつかの関数があります。

各関数は、結合された配列への参照である「ジェネレーター」を返します。結合された配列を直接使用することも、イテレータなどのアクセサ メソッドを使用することもできます。

于 2010-01-25T22:14:40.603 に答える
3

イテレータを使用するか、CPANのTie :: LazyListの使用を検討してください(これは少し古いです)。

于 2008-09-21T01:08:25.127 に答える
3

私はperlmonks.orgで同様の質問をしましたが、BrowserUk は彼の回答で非常に優れたフレームワークを提供しました。基本的に、遅延評価を取得する便利な方法は、少なくとも結果が必要であることが確実である限り、計算用のスレッドを生成することです。遅延を減らすためではなく、計算を避けるために遅延評価が必要な場合、プル モデルではなくプッシュ モデルに依存しているため、私のアプローチは役に立ちません。おそらくCoroルーチンを使用して、このアプローチを (シングルスレッドの) プル モデルに変換することもできます。

この問題を考えながら、スレッドの結果に配列を結びつけることで、Perl プログラムの流れをよりmap.parallel結果に対するメソッド。コードのより文書化されたバージョンは、そのスレッドへの返信として投稿され、おそらく CPAN にもリリースされます。

于 2008-09-22T08:42:49.907 に答える
2

私が正しく覚えていれば、for / foreachはとにかく最初にリスト全体を取得するので、遅延評価されたリストは完全に読み取られ、次に要素を反復処理し始めます。したがって、whileループを使用する以外に方法はないと思います。しかし、私は間違っているかもしれません。

whileループの利点は、コード参照を使用して遅延評価されたリストの感覚を偽造できることです。

my $list = sub { return calculate_next_element };
while(defined(my $element = &$list)) {
    ...
}

結局のところ、私はあなたがPerl5で得ることができるのと同じくらい近いと思います。

于 2008-09-21T00:56:39.163 に答える