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

8
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

这些子程序都返回一个数组,但返回的数组行为不同。在文档中表明清楚哪种类型的数组是由子程序返回的,应该使用哪个正确的表达式?
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 ...

3
它们表现不同,因为你返回了不同的内容。第一个是数组——一系列东西的列表;而第二个返回标量——单一的东西——它碰巧包含一个数组。请参考https://docs.raku.org/language/containers#Scalar_containers_and_listy_things。 - Holli
1
@Holli:我知道我正在返回不同的东西,它们的行为也不同。 - sid_com
2个回答

9

这篇文章主要讲述的是一个未去容器化的 Scalar

你可以将return $d修改为return @$d

有一个选项可以获得相同的行为,即修改b例程。

你写到“这些子例程都返回一个数组,但是返回的数组行为不同。”,但是如Holli所指出的那样,b实际上返回了绑定到$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返回的值进行去容器化,则会出现第二个问题/机会。

无论b返回什么,都将评估分配给@f的值列表。在列表上下文中,Scalar保持不变(就像使用简单的return $d一样)。因此,如果您不更改b以消除容器,则需要在分配给@f时进行,如果要使@e@f最终相同。

这次你不能只是添加@就行了。因为那会拼写成@b,Raku会将其解释为一个@b变量。

一种方法是编写@f = @(b()),但这样会显得丑陋/非惯用语。另一种选择是编写@f = b[]。这利用了b调用中的括号是多余的事实。附加[]("禅片")具有与编写@(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

"明确文档中的内容"

在文档中,如何表述清楚子程序返回的内容是哪种数组类型?

即使将问题转换为"返回的是什么",我也不确定您的意思。

我也不确定应该在文档中指向什么地方,甚至不确定是否有任何与您在 Stack Overflow 场景相关的好的指向。

我知道如果是我的话,相对于您的场景,我会发现以下文档部分比较困惑:

  • Holli 提供的 Scalar containers and listy things 部分。对我来说,该部分目前似乎涉及在列表/数组中使用 Scalar 容器,这与我上述第二个问题($d 在赋值给 @f 的列表中)相关。但这与我所提到的第一个问题无关(从 b 子例程中返回 $d)。在那种情况下,情况恰恰相反,即一个数组在 Scalar 内部。

  • 同一页中的 Scalar containers 部分。开头的句子——"尽管 Raku 中到处都是 Scalar 类型的对象,但您很少直接看到它们作为对象,因为大多数操作会取消容器化..." 对我来说没问题。但是 "如果将其标记为 is rw,则例程可以返回容器" 更具有争议性。这是真的:

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

但是,并不一定需要将例程标记为 is rw 才能返回容器。可以使用 return 例程,就像您所做的那样:

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

1
我将按照您的建议对变量进行去容器化($d[])。 在文档中,我只写了子程序返回一个数组。对于用户来说,找出数组是如何返回的并不难。 - sid_com
3
睡了一晚上后,特别是如果你正在编写与return @d对齐的文档,我建议为了列出上下文来说,应该将后者写为return @$d而不是$d[]。如果用户使用文档网站搜索字段搜索文档中的@,则目前的第二个条目是“@ list contextualizer”,这是正确的信息,而如果搜索文档中的[],则唯一接近的条目是最后一个,但在我看来它真的没有帮助。实际上,新手会怎么知道要搜索“Zen slices”呢? - raiph
1
嗨,我觉得最后两个片段实现了相同的功能,但是最后一个中的 f() = 84 出错了。但我认为重点是两者都返回一个容器,但其中一个恰好是可读写的;是这样吗?此外,return-rw文档中写道:“子例程 return 将返回值,而不是容器。”;我想这不正确,因为你展示的是另一种情况,但我可能在术语方面有所遗漏。 - Mustafa Aydın
1
@MustafaAydın“[也许] returnreturn-rw 都返回一个容器,但其中一个恰好是可读写的?” ➊ 我希望有人能像你一样发表评论。我们的评论现在简明地介绍了与此相关的问题,这让我感到高兴。所以,首先,谢谢!➋ 在这两种情况下,它肯定是一个标量。➌ 可能文档编写者更喜欢将只读的标量称为值,因为从引用透明度的角度来看,它们是等价的。➍ 我预测有一天会有人发布关于这些问题的问题... ;) - raiph

1
原问题询问以下错误为何发生:
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):
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

这里发生了什么?
Well,raku在测试类型之前会自动去除标量$x中的容器——这就是Scalar容器的工作方式。因此,您应该使用具体的返回类型约束,如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绕过标量容器的正常透明去污染,让你测试返回类型是否是顶级容器。因此(我相信你已经从raiph更好的答案中了解到),请将--> Scalar作为一种罕见形式而非常见的--> Array检查进行记录。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接