如何在 Perl 6 中使用非缓存无限惰性列表
How can I use a non-caching infinite lazy list in Perl 6
无限惰性列表真棒!
> my @fibo = 0, 1, *+* ... *;
> say @fibo[1000];
43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875
它们会自动缓存它们的值,这在大多数情况下都很方便。
但是当处理巨大的斐波那契数 (example) 时,这可能会导致内存问题。
不幸的是,我不知道如何创建非缓存斐波那契数列。有人吗?
一个主要问题是您将它存储在一个数组中,这当然保留了它的所有值。
接下来的问题有点微妙,dotty sequence generator syntax <i>LIST</i>, <i>CODE</i> ... <i>END</i>
不知道CODE
部分要将前面的值取多少要求,所以它保留了所有这些。
(它可以查看 CODE
的 arity/count,但目前似乎没有来自 REPL 的实验)
那么问题是使用 &postcircumfix:<[ ]>
on a Seq calls .cache
假设你会在某个时候要求另一个值。
(通过查看 Seq.AT-POS 的来源)
未来的实施可能会更好地解决这些缺点。
您可以使用不同的功能创建序列,以绕过 dotty 序列生成器语法的当前限制。
sub fibonacci-seq (){
gather {
take my $a = 0;
take my $b = 1;
loop {
take my $c = $a + $b;
$a = $b;
$b = $c;
}
}.lazy
}
如果您只是遍历值,您可以按原样使用它。
my $v;
for fibonacci-seq() {
if $_ > 1000 {
$v = $_;
last;
}
}
say $v;
my $count = 100000;
for fibonacci-seq() {
if $count-- <= 0 {
$v = $_;
last;
}
}
say chars $v; # 20899
您也可以直接使用 Iterator。虽然在大多数情况下这不是必需的。
sub fibonacci ( UInt $n ) {
# have to get a new iterator each time this is called
my \iterator = fibonacci-seq().iterator;
for ^$n {
return Nil if iterator.pull-one =:= IterationEnd;
}
my \result = iterator.pull-one;
result =:= IterationEnd ?? Nil !! result
}
如果您有足够新的 Rakudo 版本,您可以使用 skip-at-least-pull-one
。
sub fibonacci ( UInt $n ) {
# have to get a new iterator each time this is called
my \result = fibonacci-seq().iterator.skip-at-least-pull-one($n);
result =:= IterationEnd ?? Nil !! result
}
您也可以直接实现 Iterator class,将其包装在 Seq.
中
(这主要是 return 序列在 Rakudo 核心中编写的方法)
sub fibonacci-seq2 () {
Seq.new:
class :: does Iterator {
has Int $!a = 0;
has Int $!b = 1;
method pull-one {
my $current = $!a;
my $c = $!a + $!b;
$!a = $!b;
$!b = $c;
$current;
}
# indicate that this should never be eagerly iterated
# which is recommended on infinite generators
method is-lazy ( --> True ) {}
}.new
}
显然,菜鸟不能发表评论。
定义惰性迭代器(如 sub fibonacci-seq2)时,应通过添加 "is-lazy" 方法将迭代器标记为惰性,该方法 returns 为真,例如:
method is-lazy(--> True) { }
这将使系统能够更好地检测可能的无限循环。
无限惰性列表真棒!
> my @fibo = 0, 1, *+* ... *;
> say @fibo[1000];
43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875
它们会自动缓存它们的值,这在大多数情况下都很方便。 但是当处理巨大的斐波那契数 (example) 时,这可能会导致内存问题。
不幸的是,我不知道如何创建非缓存斐波那契数列。有人吗?
一个主要问题是您将它存储在一个数组中,这当然保留了它的所有值。
接下来的问题有点微妙,dotty sequence generator syntax <i>LIST</i>, <i>CODE</i> ... <i>END</i>
不知道CODE
部分要将前面的值取多少要求,所以它保留了所有这些。
(它可以查看 CODE
的 arity/count,但目前似乎没有来自 REPL 的实验)
那么问题是使用 &postcircumfix:<[ ]>
on a Seq calls .cache
假设你会在某个时候要求另一个值。
(通过查看 Seq.AT-POS 的来源)
未来的实施可能会更好地解决这些缺点。
您可以使用不同的功能创建序列,以绕过 dotty 序列生成器语法的当前限制。
sub fibonacci-seq (){
gather {
take my $a = 0;
take my $b = 1;
loop {
take my $c = $a + $b;
$a = $b;
$b = $c;
}
}.lazy
}
如果您只是遍历值,您可以按原样使用它。
my $v;
for fibonacci-seq() {
if $_ > 1000 {
$v = $_;
last;
}
}
say $v;
my $count = 100000;
for fibonacci-seq() {
if $count-- <= 0 {
$v = $_;
last;
}
}
say chars $v; # 20899
您也可以直接使用 Iterator。虽然在大多数情况下这不是必需的。
sub fibonacci ( UInt $n ) {
# have to get a new iterator each time this is called
my \iterator = fibonacci-seq().iterator;
for ^$n {
return Nil if iterator.pull-one =:= IterationEnd;
}
my \result = iterator.pull-one;
result =:= IterationEnd ?? Nil !! result
}
如果您有足够新的 Rakudo 版本,您可以使用 skip-at-least-pull-one
。
sub fibonacci ( UInt $n ) {
# have to get a new iterator each time this is called
my \result = fibonacci-seq().iterator.skip-at-least-pull-one($n);
result =:= IterationEnd ?? Nil !! result
}
您也可以直接实现 Iterator class,将其包装在 Seq.
中
(这主要是 return 序列在 Rakudo 核心中编写的方法)
sub fibonacci-seq2 () {
Seq.new:
class :: does Iterator {
has Int $!a = 0;
has Int $!b = 1;
method pull-one {
my $current = $!a;
my $c = $!a + $!b;
$!a = $!b;
$!b = $c;
$current;
}
# indicate that this should never be eagerly iterated
# which is recommended on infinite generators
method is-lazy ( --> True ) {}
}.new
}
显然,菜鸟不能发表评论。
定义惰性迭代器(如 sub fibonacci-seq2)时,应通过添加 "is-lazy" 方法将迭代器标记为惰性,该方法 returns 为真,例如:
method is-lazy(--> True) { }
这将使系统能够更好地检测可能的无限循环。