如何使用已定义的生成器构建惰性列表,是否有"takeWhile"替代方案?

Jes*_*wak 4 perl6

在懒惰的列表上阅读perl6intro,这让我对某些事情感到困惑.

举个例子:

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

my $alist = (1,2, &foo ... ^ * > 100);
Run Code Online (Sandbox Code Playgroud)

会给我(1 2 4 16 256),它将相同的数字直到它超过100.我希望这给我(1 4 9 16 25 .. ),所以不是平方相同的数字,将数字提前x1(或另一个给定的"步骤"),foo x等等.

在这种特定情况下是否有可能实现这一目标?

我在懒惰列表上的另一个问题如下:在Haskell中,有一个takeWhile函数,在Perl6中是否存在类似的东西?

Bra*_*ert 5

以下是编写Perl 6等效Haskell的方法takewhile.

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
}
Run Code Online (Sandbox Code Playgroud)

我可能也应该展示一个实现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
}
Run Code Online (Sandbox Code Playgroud)

这些只是刚开始工作的例子.

可以说这些值得添加为列表/可迭代的方法.

您可以(但可能不应该)使用序列生成器语法实现这些.

sub take-while ( &condition, Iterable \sequence ){
  my \iterator = sequence.iterator;
  my \generator = { iterator.pull-one } …^ { !condition $_ }
  sequence.is-lazy ?? generator.lazy !! generator
}
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)

如果它们被添加到Perl 6/Rakudo中,它们很可能会用Iterator类实现.
(我可能会去添加它们.)


直接实现您要求的是:

do {
  my $x = 0;
  { (++$x)² } …^ * > 100
}
Run Code Online (Sandbox Code Playgroud)

可以使用状态变量完成:

{ ( ++(state $x = 0) )² } …^ * > 100
Run Code Online (Sandbox Code Playgroud)

并且在声明它之外不使用的状态变量不需要名称.
(标量变量以未定义的Any开头,在数值上下文中变为0)

{ (++( $ ))² } …^ * > 100
{ (++$)² } …^ * > 100
Run Code Online (Sandbox Code Playgroud)

如果需要初始化匿名状态变量,可以使用//与等数元运算符组合的defined-or运算符=.

{ (++( $ //= 5))² } …^ * > 100
Run Code Online (Sandbox Code Playgroud)

在一些简单的情况下,您不必告诉序列生成器如何计算下一个值.
在这种情况下,结束条件也可以简化.

say 1,2,4 ...^ 100
# (1 2 4 8 16 32 64)
Run Code Online (Sandbox Code Playgroud)

唯一可以安全地简化结束条件的时间是,如果您知道它将在值上停止.

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 ...)
Run Code Online (Sandbox Code Playgroud)

  • @learningProgged 为什么不可能?这并不是真正的使用方式,但请谨慎对待,因为我可能比其他任何人都更多地误用了该功能来编写代码高尔夫条目。在某些情况下,您可能希望将其标记为惰性 `({ foo ++$ } ...^ * > 100).lazy` (2认同)

sml*_*mls 5

我想要这个给我 (1 4 9 16 25 ..)

获得该序列的最简单方法是:

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

...或者如果您更喜欢以类似于 Haskell/Python 列表推导式的方式编写它:

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

虽然可以通过...运算符不遗余力地表达这个序列(正如Brad Gilbert 的回答raiph 的回答所展示的那样),但这并没有什么意义,因为该运算符的目的是生成每个元素所在的序列使用一致的规则从先前的元素派生。

为每项工作使用最好的工具:

  • 如果一个序列最容易迭代表达 (例如斐波那契数列)
    使用...运算符。

  • 如果一个序列最容易表达为一个封闭的公式 (例如平方序列)
    使用mapfor