2016-12-17 5 views
3

私はperl6intro on lazy listsを読んでいるので、特定のことについて混乱してしまいます。定義されたジェネレータを使用して遅延リストを構築する方法はありますか? "takeWhile"代替手段はありますか?

この例を見てみましょう:

sub foo($x) { 
    $x**2 
} 

my $alist = (1,2, &foo ...^* > 100); 

は私に(1 2 4 16 256)を与えるだろう、それはそれは私はこれが私に(1 4 9 16 25 ..)を与えたい100を超えるまで同じ数を二乗するので、代わりに同じ数の二乗の、前進します数字xを1(または別の「ステップ」)、foo xなどとする。

この特定のケースでこれを達成することは可能ですか?

遅延リストにあるもう1つの質問は、次のとおりです。 ハスケルでは、takeWhile関数がありますが、Perl6にも同様のことがありますか?

答えて

5

ここでは、Haskellのtakewhileと同等のPerl 6を書く方法を示します。

sub take-while (&condition, Iterable \sequence){ 
    my \iterator = sequence.iterator; 

    my \generator = gather loop { 
    my \value = iterator.pull-one; 
    last if value =:= IterationEnd or !condition(value); 
    take value; 
    } 

    # should propagate the laziness of the sequence 
    sequence.is-lazy 
    ?? generator.lazy 
    !! generator 
} 

おそらくdropwhileの実装を示す必要があります。

sub drop-while (&condition, Iterable \sequence){ 
    my \iterator = sequence.iterator; 

    GATHER: my \generator = gather { 

    # drop initial values 
    loop { 
     my \value = iterator.pull-one; 

     # if the iterator is out of values, stop everything 
     last GATHER if value =:= IterationEnd; 

     unless condition(value) { 
     # need to take this so it doesn't get lost 
     take value; 

     # continue onto next loop 
     last; 
     } 
    } 

    # take everything else 
    loop { 
     my \value = iterator.pull-one; 
     last if value =:= IterationEnd; 
     take value 
    } 
    } 

    sequence.is-lazy 
    ?? generator.lazy 
    !! generator 
} 

これらは、単なる取得例です。

リスト/イテラブルにメソッドとして追加する価値があると主張できます。

これらはシーケンスジェネレータの構文で実装できます(ただし、そうしてはいけません)。

sub take-while (&condition, Iterable \sequence){ 
    my \iterator = sequence.iterator; 
    my \generator = { iterator.pull-one } …^ { !condition $_ } 
    sequence.is-lazy ?? generator.lazy !! generator 
} 
sub drop-while (&condition, Iterable \sequence){ 
    my \end-condition = sequence.is-lazy ?? * !! { False }; 
    my \iterator = sequence.iterator; 

    my $first; 
    loop { 
    $first := iterator.pull-one; 
    last if $first =:= IterationEnd; 
    last unless condition($first); 
    } 

    # I could have shoved the loop above into a do block 
    # and placed it where 「$first」 is below 

    $first, { iterator.pull-one } … end-condition 
} 

彼らはPerl 6の/ Rakudoに追加された場合、彼らはおそらくイテレータクラスで実装されるだろう。

:状態変数で行うことができます

do { 
    my $x = 0; 
    { (++$x)² } …^ * > 100 
} 

:あなたが求めているものの
(。私はちょうどそれらを行くと追加される場合があります)


直接実装のようなものです

{ (++(state $x = 0))² } …^ * > 100 

そして、宣言の外では使用されない状態変数は名前を必要としません。
(スカラー変数は、数値コンテキストで0になる、任意の未定義として開始)

{ (++($))² } …^ * > 100 
{ (++$)² } …^ * > 100 

匿名状態変数を初期化する必要がある場合、あなたはと組み合わせて定義された、またはオペレータ//を使用することができますが等しいメタオペレータ=。あなたは次の値を計算する方法をシーケンスジェネレータを伝える必要はありませんいくつかの簡単な例で

{ (++($ //= 5))² } …^ * > 100 


このような場合、終了条件を簡素化することもできます。あなたはそれが価値に停止しますを知って場合

say 1,2,4 ...^ 100 
# (1 2 4 8 16 32 64) 

あなたが安全に終了条件を簡素化することができる唯一の他の時間があります。

say 1, { $_ * 2 } ... 64; 
# (1 2 4 8 16 32 64) 

say 1, { $_ * 2 } ... 3; 
# (1 2 4 8 16 32 64 128 256 512 ...) 
+0

がtakeWhileとdropWhile実装例をありがとうとして、それが効果的に同じであるので、 。ジェネレータについて:この場合、名前付き関数を使用することはできないと仮定するのは当然ですか? '{foo(++ $ x)} ...^*> 100'と似ています –

+1

@learningProggedなぜそれは可能ではありませんか?それは実際に使用される意味ではありませんが、私はおそらくコードのゴルフのエントリを書くために他の誰よりもその機能を悪用したとして塩の粒でそれを取る。いくつかの場合 '({foo ++ $} ...^*> 100).lazy'として怠け者としてマークしたいかもしれません –

3

私は、これは私を与えたい(1 4 9 16 25 ..)

my @alist = {(++$)²} ... Inf; 
say @alist[^10]; # (1 4 9 16 25 36 49 64 81 100) 

{...}は、コードの任意のブロックです。 the ... sequence operatorのLHSとして使用すると、シーケンスの各値に対して呼び出されます。

中かっこの中の(...)²は、括弧内の式の2乗になります。 (私は同じことを意味する(...) ** 2を書かれている可能性があります。)

++$戻り1, 2, 3, 4, 5, 6 ...a $ variableで事前インクリメント++を(1を加算)組み合わせることで。

ハスケルには、takeWhile関数がありますが、Perl6にも同様のことがありますか?

所望の最終状態に上記のシーケンスからInfを置き換えます。

my @alist = {(++$)²} ... * > 70; # stop immediately after past 70 
say @alist; # [1 4 9 16 25 36 49 64 81] 

my @alist = {(++$)²} ...^ * > 70; # stop immediately before past 70 
say @alist; # [1 4 9 16 25 36 49 64] 

(配列オペレータの......^変異体は停止条件の二つの意味を提供する方法に注意してください私は注意あなたのオリジナルの質問には...^* > 70があります。この中の^...から切り離されていますので、意味が分かりませんが、私は寝る必要があります:))

+3

'...^*> 70'は(^ *)...'と同じである> 70' '*> 70' –

4

私はこれが私を与えたい(1 4 9 16 25 ...)

そのシーケンスを取得する最も簡単な方法、次のようになります。

my @a = (1..*).map(* ** 2); # using a Whatever-expression 
my @a = (1..*).map(&foo); # using your `foo` function 

..あなたがHaskell/Pythonのリスト理解に似たやり方で書くことを好むなら、それは:

my @a = ($_ ** 2 for 1..*); # using an in-line expression 
my @a = (foo $_ for 1..*); # using your `foo` function 

...演算子(Brad Gilbert's answerraiph's answerのデモンストレーション)を使用してこのシーケンスを表現することはできますが、実際には意味をなさないのですが、その演算子の目的は各要素が一貫した規則を使用して前の要素から派生したものです。配列が反復(例えば、フィボナッチ数列)を発現することが最も容易である場合

  • は、各ジョブのための最良のツールを使用し
    ...演算子を使用します。

  • 配列が閉鎖式(正方形の例えば配列)として表現するのが最も簡単である場合:
    map使用又はfor

関連する問題