如何从rakusub返回2个哈希?
...
#!/usr/bin/env raku
# -*-perl6-*-
# 2021-5-30: Example of how a sub does not seem to be able to return 2 Hashes...
sub GetHashes
{
my %H = 100 => 2149, 101 => 2305, 102 => 2076, 103 => 1767, 104 => 1743 ;
my %G = 100 => 21493, 101 => 23053, 102 => 20763, 103 => 17673, 104 => 17433 ;
return( %H, %G );
}
my ( %H, %G ) = GetHashes ;
%H.say;
%G.say;
...
我是一个老的 FORTRAN/perl5 程序员,他决定尝试学习 raku。上面的代码不起作用。我错过了什么?在 perl5 中,我会将 ref 返回到散列,并在调用者中取消 ref。这里是什么?
回答
我错过了什么?
许多可能的答案中有两个答案是:
-
你少了一个冒号。
具体来说,您可以更改:
my ( %H, %G ) = GetHashes ;my ( %H, %G ) = GetHashes ;到:
my ( %H, %G ) := GetHashes ; ^
-
你错过了@p6steve 的回答。
具体来说,您可以更改:
到:
my ( $H, $G ) = GetHashes ;
总而言之,Raku 支持多种方式来使某些存储位置“包含”某些数据或“引用”某些数据。您正在使用“列表分配”,但需要使用其中一种替代方法,例如我们的答案。
分配 ( =)
分配意味着将数据复制到一些最终“包含”复制数据的存储位置。[1]
如果您使用列表分配,则列表中的项目越多,复制的字节就越多。如果您有一百万个项目,则不太可能使用列表分配来分配它们。正如您所发现的,可能还有其他原因不想使用赋值。
例如,考虑my @array1 = 1, 2. 这段代码:
-
构造
List两个元素的 a --(1, 2)-- 作为要分配/复制的数据; -
构造一个新的空
Array并将这个新数组“绑定”到“符号”(变量名)@array1; -
迭代
List的元素,对于每个元素:3.1
Scalar在目标数组中添加一个新的对应的空“容器”(a ),准备接收一些标量数据;3.2 将一个标量(单个)值从 复制
List到Scalar数组中的新容器中。
请注意这最终如何在运行时创建三个存储位置:一个数组和两个单独的Scalars 作为该数组的前两个可索引元素。所有这三个存储位置都会更新,数组中Scalar添加了两个Scalars ,两个s 接收从列表中复制的两个值。
绑定 ( :=)
绑定意味着将引用(指针)复制到数据。数据本身不会被复制。如果有,比如说,对数据的两次引用,那么无论数据有多大,都不会被复制。坚持复制引用而不是被引用的数据的一种方法是使用绑定。
考虑,例如my @array2 := 1, 2。这段代码:
-
构造
List两个元素的 a(1, 2); -
将对 的引用绑定
List到“符号”(变量名)@array2。
请注意,与上面的赋值示例不同,此绑定示例在被更新的左侧只有一个存储位置 ( @array2) :=,对应于右侧被视为单个值 (a List)。
绑定操作的左侧可以有多个存储位置。考虑你的例子,my ( %H, %G ) := GetHashes;。这段代码:
-
求值
GetHashes,返回List两个散列中的一个; -
迭代对值/引用列表正确的
:=,对每个反过来结合目标存储位置的列表中留下的:=。
请注意,与前面的绑定示例不同的是,这个绑定示例最终会更新两个存储位置(与符号%H和关联%G),对应于调用List返回的两个散列GetHashes。
原始代码中列表赋值的效果
使用您的代码,您将右侧的所有数据/值=逐项复制到与左侧第一个变量 ( %H)绑定的哈希中.
这是由于赋值的精确语义(使用 of =)由 左侧的各个目标确定=,从第一个开始,直到填充完毕,然后是第二个,依此类推。
如果分配目标是Associative(eg %H),它会贪婪地从=. 这称为“列表分配”。
因此,您的原始代码发生的情况是将%H返回的第一个散列中的前五个元素吸满GetHashes,然后继续前进,因此sub 返回的第二个散列中的五个元素也分配给%H。
并且因为第二批对的密钥与第一批的密钥相同,所以第二批会覆盖前五个。
所以%H最终只有第二批对,并%G保持空,结果你看到了。
类似的交易适用于Positional赋值目标,例如名为 的变量@array。
标准 Raku 中唯一不以这种方式贪婪的分配目标Scalar是$bar. 因此,您可以有效地编写、说my ($bar, @bam) = 1, 2, 3;并以$bar分配的方式结束1,并@bam以[2 3]. 因此@p6steve 的回答。
脚注
[1]如果=在某些代码中使用,但左侧的容器赋值不是该表达式的意思,Raku 会按照作者可能的意思来代替。例如:
-
constant list = 1, 2, 3;并没有暂时分配List(1, 2, 3)到符号list在运行时,而是永久地结合在编译时它。换句话说,它与constant list := 1, 2, 3;. (大多数人只是写constant list = 1, 2, 3;。) -
my int @list = 1, 2, 3;不会Scalar向本机数组添加任何容器,@list而是直接写入本机整数。