【问题标题】:How does lazyness of the slice index affects the slicing of an array/list? [RAKU]切片索引的惰性如何影响数组/列表的切片? [乐]
【发布时间】:2021-01-15 04:24:40
【问题描述】:

当我们使用超出数组边界的索引对数组进行切片时,我们会得到未定义 (Any) 的结果

当我们将相同的切片索引作为惰性列表传递时,我们会得到数组/列表的现有值(仅此而已):

my @a = ^5;

say @a[^10];        # (0 1 2 3 4 (Any) (Any) (Any) (Any) (Any))
say @a[lazy ^10];   # (0 1 2 3 4)

显然切片索引的惰性会影响结果。

试图理解事物的本质,作为概念证明,我编写了我的切片机制的简单版本:

my @a = ^5;

my @s1 = ^10;
my @s2 = lazy ^10;

sub postcircumfix:<-[ ]-> (@container, @index) {
    my $iter = @index.iterator;

    gather {
        loop {
            my $item := $iter.pull-one;

            if $item =:= IterationEnd {
                last;
            }

            with @container[$item] {
                take @container[$item]
            } else {
                @index.is-lazy ?? { last } !! take @container[$item];
            }
        }
    }
}

say @a-[@s1]-;   # (0 1 2 3 4 (Any) (Any) (Any) (Any) (Any))
say @a-[@s2]-;   # (0 1 2 3 4)

但我想知道我的幼稚算法是否描述了事物在幕后的计算方式!

【问题讨论】:

  • 好问题@jakar - 在我看来,这也说明了 raku 设计中的思想及其包容理念。一种更具约束性的语言会“这是正确的方法,要么(i)总是在数组末尾切断切片器 - 要么 - (ii)总是迭代到切片器的末尾”。这使用“惰性”迭代来巧妙地解决无限切片器问题 - 并且 - 如果这是我们需要的,它为我们提供了一种简洁的方法来做 (i)。

标签: slice raku lazy-sequences rakudo


【解决方案1】:

可以在array_slice.pm6 中找到有关事情如何完成的源代码。

具体可以在L73看到以下内容:

    if is-pos-lazy {
        # With lazy indices, we truncate at the first one that fails to exists.
        my \rest-seq = Seq.new(pos-iter).flatmap: -> Int() $i {
            nqp::unless(
              $eagerize($i),
              last,
              $i
            )
        };
        my \todo := nqp::create(List::Reifier);
        nqp::bindattr(todo, List::Reifier, '$!reified', eager-indices);
        nqp::bindattr(todo, List::Reifier, '$!current-iter', rest-seq.iterator);
        nqp::bindattr(todo, List::Reifier, '$!reification-target', eager-indices);
        nqp::bindattr(pos-list, List, '$!todo', todo);
    }
    else {
        pos-iter.push-all: target;
    }

所以,正如您所推测的,它确实会在列表项不存在后停止。这无疑是因为许多惰性列表是无限的,并且迭代器不提供一种方法来知道它们是否是无限的(生成器可能是非确定性的)。

如果您真的想启用这样的功能,例如,您可以编写自己的切片器来处理可能不可用的元素的惰性列表,但您必须注意确保只有在以下情况下才会急切地评估事物你知道它们是有限的:

multi sub postcircumfix:<-[ ]-> (@a, @b) {
  lazy gather {
    take @a[$_] for @b;
  }
}

my @a = ^5;
my @b = lazy gather { 
  for ^10 -> $i { 
    # So we can track when elements are evaluated
    say "Generated \@b[$i]"; 
    take $i;
  } 
};

say "Are we lazy? ", @a-[@b]-;
say "Let's get eager: ", @a-[@b]-.eager;
say "Going beyond indices: ", @a-[@b]-[11]

这个输出是

Are we lazy? (...)
Generated @b[0]
Generated @b[1]
Generated @b[2]
Generated @b[3]
Generated @b[4]
Generated @b[5]
Generated @b[6]
Generated @b[7]
Generated @b[8]
Generated @b[9]
Let's get eager: (0 1 2 3 4 (Any) (Any) (Any) (Any) (Any))
Going beyond indices: Nil

【讨论】:

  • 感谢代码参考! (只有一个小故障!在代码的第 12 行中,“take $i++”必须是:“take $i”)
  • @jakar:哎呀,是的。这就是我在复制和粘贴后编辑代码得到的结果。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-01-19
  • 2018-08-20
  • 2017-07-03
  • 1970-01-01
  • 2012-11-11
  • 2012-11-10
  • 2021-05-30
相关资源
最近更新 更多