如何指定返回的数组类型?

How to specify a returned Array type?

sub a { my @c = 1, 2, 3, 4;     return @c };
sub b { my $d = [ 1, 2, 3, 4 ]; return $d };

say a().WHAT; # (Array)
say b().WHAT; # (Array)

my @e = a();
my @f = b();

say @e;     # [1 2 3 4]
say @e[1];  # 2

say @f;     # [[1 2 3 4]]
say @f[1];  # (Any)

# valid raku code, no errors messages

这些子例程都 return 一个数组,但 returned 数组的行为不同。在文档中明确哪种数组类型被子例程 return 编辑的正确表达式是什么?

sub b ( --> Array ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT;       # (Array)
sub b ( --> Scalar ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT;

# Type check failed for return value; expected Scalar but got Array ([1, 2, 3, 4])
#   in sub b at ...
#   in block <unit> at ...

这都是关于一个尚未去容器化的 Scalar

您可以将 return $d 更改为 return @$d

获得相同行为的一个选择是改变 b 例程。

您已经写了“这些子例程都是 return 一个数组,但是 returned 数组的行为不同。”。但是,正如 Holli 指出的那样,b 而不是 return 绑定到 $dScalar(后者又包含一个数组):

sub b { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().VAR.WHAT; # (Scalar)

您可以通过“去容器化”$d 来更改它,例如通过添加 @:

sub b { my $d = [ 1, 2, 3, 4 ]; return @$d };
say b().VAR.WHAT; # (Array)

my @f = b();

say @f;     # [1 2 3 4]
say @f[1];  # 2

或将@f = b()改为@f = b[]

如果您去容器化由b编辑的值return,那么就会出现第二个问题/机会。

不管 b return 是什么,@f 赋值将评估分配给它的值列表。在列表上下文中,Scalars 保持原样(就像它们与普通 return $d 一样)。因此,如果您 而不是 b 更改为去容器化,那么您需要在对 @f 的赋值中这样做,如果您想要 @e@f 结束相同。

这一次你不能只在前面加上 @ 来这样做。因为那会拼写 @b —— Raku 会将其解释为 @b 变量。

一种选择是写成 @f = @(b()),但那会很丑陋/不符合习惯。另一种选择是写 @f = b[]。这利用了 b 调用中的括号是多余的这一事实。添加 [](一个“zen slice”)与写入 @(b) 具有相同的效果,但少了一个字符。

因此,要在列表分配中去容器化,您可以这样写:

sub b { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().VAR.WHAT; # (Scalar)

my @f = b[];

say @f;     # [1 2 3 4]
say @f[1];  # 2

“在文档中明确”

What would be the correct expression to make clear in the documentation which array type what is returned by the subroutine?

我不确定你问这个问题是什么意思,即使只是切换到“什么是 returned”。

我也不确定在文档中向您指出什么,即使有任何好的地方可以向您指出,相对于您的 SO 中的场景。

我确实知道,如果是我,相对于您的情况,我会发现以下文档部分 令人困惑

  • Scalar containers and listy things 部分 Holli 已链接。在我看来,该部分目前是关于在 lists/arrays 中使用 Scalar 容器,这与我在上面写的 second 问题相关($d 在分配给 @f) 的右侧列表中。但这与我写的第一个问题无关(return $d 来自 b 例程)。事情是相反的,即 Scalar.

    中有一个数组
  • 同一页前面的 Scalar containers 部分。开场白——“尽管 Scalar 类型的对象在 Raku 中无处不在,但你很少将它们直接视为对象,因为大多数操作都会去容器化……”对我有用。但是“一个例程可以 return 一个容器,如果它被标记为 is rw” 是更有问题的。 真:

my $x = 23;
sub f() is rw { $x };
f() = 42;
say $x;                 # OUTPUT: «42␤»

但是没有将例程is rw标记为return容器。可以像您一样使用 return 例程:

my $x = 23;
sub f() { return $x };
say f().VAR.WHAT;       # OUTPUT: «Scalar␤»

原题问为什么会出现如下错误:

sub b ( --> Scalar ) { my $d = [ 1, 2, 3, 4 ]; return $d };
say b().WHAT;

# Type check failed for return value; expected Scalar but got Array ([1, 2, 3, 4])
#   in sub b at ...
#   in block <unit> at ...

此答案旨在详细说明 return 类型的约束,如记录 here

如果你这样做,现在你正在 returning 一个标量:

sub b( --> Scalar ) { my $x=42; my $d = $x; return $d };
say b().WHAT;

你得到一个不同的错误

Type check failed for return value; expected Scalar but got Int (42)
  in sub b at scum.raku line 2
  in block <unit> at scum.raku line 7

这是怎么回事?

好吧,raku 在测试类型之前会自动去容器化标量 $x - 这就是标量容器的工作方式。所以 运行 的工厂代码你应该使用具体的 return 类型约束,比如 Int、Real、Num、IntStr、Allomorph、Cool 等等。 Raku 内置类型经过精心设计,可以让编码人员精确控制此测试的级别。

但是 - 如果你想玩强力技巧,那么你可以像这样应用 --> Scalar 约束(如@raiph所述):

sub b( --> Scalar ) { my $x=42; my $d = $x.VAR; return $d };
say b().WHAT;   #(Scalar)

换句话说,meta 方法 .VAR 绕过标量容器的正常透明 decont,让您测试您的 return 类型是否是顶级容器。

所以(我相信您已经从 raiph 的更好答案中了解到)请将 --> Scalar 记录为一种罕见的形式,而不是常见的 --> Array 检查。