如何使用已定义的生成器构建惰性列表,是否有 "takeWhile" 替代方案?
How to build lazy lists with defined generators and is there a "takeWhile" alternative?
我正在通读 perl6intro on lazy lists,这让我对某些事情感到困惑。
举个例子:
sub foo($x) {
$x**2
}
my $alist = (1,2, &foo ... ^ * > 100);
会给我 (1 2 4 16 256)
,它将对相同的数字进行平方,直到它超过 100。我希望这个给我 (1 4 9 16 25 .. )
,所以不是对相同的数字进行平方,而是增加一个数字 x
乘以 1(或另一个给定的 "step"),foo x
,依此类推。
在这种特定情况下是否可以实现这一点?
我对惰性列表的另一个问题如下:
Haskell中有一个takeWhile函数,Perl6中有类似的东西吗?
I want this to give me (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
来表达同样的意思。)
++$
returns 1, 2, 3, 4, 5, 6 …
通过组合预增量 ++
(加一)与 a $
variable.
In Haskell, there is a takeWhile function, does something similar exist in Perl6?
将上述序列中的 Inf
替换为所需的结束条件:
my @alist = {(++$)²} ... * > 70; # stop at step that goes past 70
say @alist; # [1 4 9 16 25 36 49 64 81]
my @alist = {(++$)²} ...^ * > 70; # stop at step before step past 70
say @alist; # [1 4 9 16 25 36 49 64]
请注意序列运算符的 ...
和 ...^
变体如何提供停止条件的两个变体。我在您的原始问题中注意到您有 ... ^ * > 70
,而不是 ...^ * > 70
。因为后者中的 ^
与 ...
是分离的,所以具有不同的含义。请参阅布拉德的评论。
这里是你如何编写 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
}
我可能还应该展示 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
}
这些只是刚刚开始工作的示例。
可以说这些值得作为方法添加到 lists/iterables。
您可以(但可能不应该)使用序列生成器语法实现这些。
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,它们可能会使用 Iterator 类 来实现。
(我可能会去添加它们。)
直接实现你所要求的是这样的:
do {
my $x = 0;
{ (++$x)² } …^ * > 100
}
可以用状态变量来完成:
{ ( ++(state $x = 0) )² } …^ * > 100
除了声明之外不使用的状态变量不需要名称。
(标量变量以未定义的 Any 开始,在数字上下文中变为 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 ...)
I want this to give me (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
虽然可以特意通过 ...
运算符来表达这个序列(如 and 所示),但它并没有真正意义,因为它的目的运算符是生成序列,其中每个元素都使用一致的规则从前一个元素派生。
为每项工作使用最好的工具:
如果一个数列最容易迭代表达 (例如斐波那契数列):
使用 ...
运算符。
如果一个数列最容易表示为闭式 (例如正方形数列):
使用 map
或 for
.
我正在通读 perl6intro on lazy lists,这让我对某些事情感到困惑。
举个例子:
sub foo($x) {
$x**2
}
my $alist = (1,2, &foo ... ^ * > 100);
会给我 (1 2 4 16 256)
,它将对相同的数字进行平方,直到它超过 100。我希望这个给我 (1 4 9 16 25 .. )
,所以不是对相同的数字进行平方,而是增加一个数字 x
乘以 1(或另一个给定的 "step"),foo x
,依此类推。
在这种特定情况下是否可以实现这一点?
我对惰性列表的另一个问题如下: Haskell中有一个takeWhile函数,Perl6中有类似的东西吗?
I want this to give me (1 4 9 16 25 .. )
my @alist = {(++$)²} ... Inf;
say @alist[^10]; # (1 4 9 16 25 36 49 64 81 100)
{…}
是任意代码块。当用作 the ...
sequence operator.
(…)²
的计算结果为括号内表达式的平方。 (我本可以写成 (…) ** 2
来表达同样的意思。)
++$
returns 1, 2, 3, 4, 5, 6 …
通过组合预增量 ++
(加一)与 a $
variable.
In Haskell, there is a takeWhile function, does something similar exist in Perl6?
将上述序列中的 Inf
替换为所需的结束条件:
my @alist = {(++$)²} ... * > 70; # stop at step that goes past 70
say @alist; # [1 4 9 16 25 36 49 64 81]
my @alist = {(++$)²} ...^ * > 70; # stop at step before step past 70
say @alist; # [1 4 9 16 25 36 49 64]
请注意序列运算符的 ...
和 ...^
变体如何提供停止条件的两个变体。我在您的原始问题中注意到您有 ... ^ * > 70
,而不是 ...^ * > 70
。因为后者中的 ^
与 ...
是分离的,所以具有不同的含义。请参阅布拉德的评论。
这里是你如何编写 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
}
我可能还应该展示 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
}
这些只是刚刚开始工作的示例。
可以说这些值得作为方法添加到 lists/iterables。
您可以(但可能不应该)使用序列生成器语法实现这些。
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,它们可能会使用 Iterator 类 来实现。
(我可能会去添加它们。)
直接实现你所要求的是这样的:
do {
my $x = 0;
{ (++$x)² } …^ * > 100
}
可以用状态变量来完成:
{ ( ++(state $x = 0) )² } …^ * > 100
除了声明之外不使用的状态变量不需要名称。
(标量变量以未定义的 Any 开始,在数字上下文中变为 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 ...)
I want this to give me (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
虽然可以特意通过 ...
运算符来表达这个序列(如
为每项工作使用最好的工具:
如果一个数列最容易迭代表达 (例如斐波那契数列):
使用...
运算符。如果一个数列最容易表示为闭式 (例如正方形数列):
使用map
或for
.