Perl 6 中嵌套和非嵌套映射的上下文有什么区别?

What is the difference in contexts in nested and non-nested maps in Perl 6?

我有这段代码。第一个,非嵌套的 map 输出一些东西,而嵌套的则没有。我想我明白为什么第二个不起作用。这是一个惰性序列,Perl 6 正在收集结果。没关系。但是第一个(非嵌套的)map 不是以同样的方式懒惰吗?如果我不对地图的结果做任何事情,它如何输出任何东西?即第一个怎么偷懒?是否会自动获取接收器上下文,我必须在其中明确提供 sink (或其他内容)给嵌套上下文?不知何故,我认为 Perl 6 应该能够为我解决这个问题。

my @array = (1, 2), (3, 4), ('a', 'b');

say "---On its own:";
my @item = 1, 2, 3;
@item.map: {
    say $_;
    };

say "---Inside another map:";
@array.map: {
    my @item = 1, 2, 3;
    @item.map: {
        say $_;
        }
    };

这是输出:

---On its own:
1
2
3
---Inside another map:

这与问题有关。那个说要做什么,但我问的更多是 为什么 问题。

在那,解决方案是添加eagersink,或分配给内部map

say "---eager:";
@array.map: {
    my @item = 1, 2, 3;
    eager @item.map: {
        say $_;
        }
    };

say "---sink:";
@array.map: {
    my @item = 1, 2, 3;
    sink @item.map: {
        say $_;
        }
    };

say "---assignment:";
@array.map: {
    my @item = 1, 2, 3;
    @ = @item.map: {
        say $_;
        }
    };

每个程序文件、块、子程序等内容都是一个"statement list",由semicolon-separated1条语句组成。考虑到这一点:

  1. 沉没的陈述:

    • 语句列表中除最后一条语句外的所有语句。
    • 任何循环语句(forwhile2)在statement-list层3,即使是最后一个。
    • 程序或模块文件的 top-level 语句列表中的任何语句,即使它是最后一个。4
  2. 返回而不是沉没的语句:

    • 语句列表中的最后一条语句,上述情况除外。

下沉力量热切评价,回归不会。

例子

在您的例子中,第一个 map 语句位于语句列表的中间,因此它 sunk.

但是嵌套的map语句是其语句列表的最后一条语句,因此其结果以not-yet-iterated[=]的形式返回 14=].

它的父map语句也是一个final语句,但是它在程序文件的top-level语句列表中,所以它得到sunk,导致它急切地迭代由三个 Seq 值组成的序列。 (在内部 map 之前插入一个 say 语句以查看此内容。)
但是,这三个内部 Seq 值中的每一个都没有下沉或以其他方式迭代。

来自设计文档

更详细,来自 Synopsis 04, line 664

In any sequence of statements, only the value of the final statement is returned, so all prior statements are evaluated in sink context, which is automatically eager, to force the evaluation of side effects. (Side effects are the only reason to execute such statements in the first place, and Perl will, in fact, warn you if you do something that is "useless" in sink context.) A loop in sink context not only evaluates itself eagerly, but can optimize away the production of any values from the loop.

The final statement of a statement list is not a sink context, and can return any value including a lazy list. However, to support the expectations of imperative programmers (the vast majority of us, it turns out), any explicit loop found as the final statement of a statement list is automatically forced to use sink semantics so that the loop executes to completion before returning from the block.

This forced sink context is applied to loops only at the statement list level, that is, at the top level of a compilation unit, or directly inside a block. Constructs that parse a single statement or semilist as an argument are presumed to want the results of that statement, so such constructs remain lazy even when that statement is a loop.


1) 当右大括号 } 出现在一行的最后一个正确标记时,如
my @a = @b.map: { $_ + 1 } # whitespace/comment doesn't count
它还会结束当前语句,但除此之外还需要分号来分隔语句。

2) map 不算,因为它是一个函数而不是循环关键字。

3) 表示当循环语句出现在不同的地方而不是直接出现在语句列表中时,例如
lazy for ^10 { .say } # as argument to a keyword expecting a single statement
(for ^10 { .say }) # inside an expression
那么默认情况下它不会沉没。这就是摘要引用的最后一段试图表达的意思。

更新:在 Rakudo 中似乎并非如此,但 may be a bug

4) 大纲中没有提到这条规则,但它在 Rakudo 中是这样运作的,我很确定这是故意的。

.map 基本上是 return 一个 .Seq。发生的情况是内部地图 return 是外部地图的 Seq,但由于该地图的结果已下沉,因此它们在没有被迭代的情况下消失了。

如果你说outer map,你会拉取inner map的结果,你会看到.seq the inner map的结果returned:

my @array = (1, 2), (3, 4), ('a', 'b');
say "---Inside another map:";
say @array.map: {
    my @item = 1, 2, 3;
    @item.map: {
        say $_;
        }
    }
---Inside another map:
1
2
3
1
2
3
1
2
3
((True True True) (True True True) (True True True))

希望说得通:-)

另一种解决方案是向外部地图添加特定的 return 值。然后内部地图将沉没,因此迭代,如下所示:

my @array = (1, 2), (3, 4), ('a', 'b');
say "---Inside another map:";
say @array.map: {
    my @item = 1, 2, 3;
    @item.map: {
        say $_;
        }
    42   # make sure ^^ map is sunk
    }
---Inside another map:
1
2
3
1
2
3
1
2
3