我在懒惰的列表上阅读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中是否存在类似的东西?
以下是编写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)
我想要这个给我 (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 的回答所展示的那样),但这并没有什么意义,因为该运算符的目的是生成每个元素所在的序列使用一致的规则从先前的元素派生。
为每项工作使用最好的工具:
如果一个序列最容易迭代表达 (例如斐波那契数列):
使用...运算符。
如果一个序列最容易表达为一个封闭的公式 (例如平方序列):
使用map或for。