【问题标题】:How can I use a non-caching infinite lazy list in Perl 6如何在 Perl 6 中使用非缓存无限惰性列表
【发布时间】:2019-11-24 20:42:24
【问题描述】:

无限惰性列表很棒!

> my @fibo = 0, 1, *+* ... *;
> say @fibo[1000];
43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875

它们会自动缓存它们的值,这很方便......大多数时候。 但是在处理巨大的斐波那契数 (example) 时,这可能会导致内存问题。

不幸的是,我不知道如何创建非缓存斐波那契数列。有人吗?

【问题讨论】:

标签: raku


【解决方案1】:

一个主要问题是你将它存储在一个数组中,当然它会保留它的所有值。

下一个问题有点微妙,dotty sequence generator syntax <i>LIST</i>, <i>CODE</i> ... <i>END</i> 不知道 CODE 部分会要求多少以前的值,所以它保留了所有这些值.
(它可以查看 CODE 的数量/数量,但目前似乎不是来自 REPL 的实验)

还有一个问题是,在 Seq 上使用 &amp;postcircumfix:&lt;[ ]&gt; 会调用 .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 类,将其包装在Seq 中。
(这主要是在 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
}

【讨论】:

  • 另一个很好的答案。 “点序列生成器 ... [缓存结果] ... &postcircumfix: 在 Seq 调用 [缓存结果]”。 @Larry 说依赖使用 @ sigil 或 explicit .cache 来表示需要缓存的缺点是什么?
  • (我说得对吗,如果规则是只有在使用@ sigil 或明确调用.cache 时才能获得缓存,那么这个 SO 问题的答案将是微不足道的优雅地更改为my $fibo = 0, 1, *+* ... *; say $fibo[1000];?)
  • "不知道 CODE 部分会要求多少以前的值,所以它保留所有这些值" -- 等等,什么?如果第二部分是真的,那在 IMO 上就很糟糕,应该报告错误。我也不同意第一部分,它无法知道要记住多少元素:它只需要查看 lambda 的签名,就像 .sort.map 查看的方式传递它们的 lambda 的签名并相应地调整它们的行为。
  • 感谢您提供广泛的解释和示例。看起来没有方便的方法来做我想做的事。我ended up 只是老式地在原地迭代自己,这是最简单的(但不是 Perl 6 很酷)的方法。
  • @raiph 我查看了 Seq.AT-POS,它调用了.cache。是否应该是另一个问题,我可以看到两者的论据。
【解决方案2】:

显然,菜鸟不能评论。

当定义一个惰性迭代器(例如 sub fibonacci-seq2)时,应该通过添加一个返回 True 的“is-lazy”方法将迭代器标记为惰性,例如:

method is-lazy(--> True) { }

这将允许系统更好地检测可能的无限循环。

【讨论】:

  • 一般来说,您至少需要 100 名代表才能发表评论。如果您提出问题,您可以随时发表评论。
  • 如果您发了一个较小的帖子,并标记版主使其成为评论,他们本可以为您完成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-12
  • 1970-01-01
  • 2013-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多