Perl 6 中的数组值散列
Hash with Array values in Perl 6
这是怎么回事?
如果 %a
有 Array
值而 %a{3}
是 Array
,为什么 %a{3}
和 %a{3}.Array
不同?
> my Array %a
{}
> %a{3}.push("foo")
[foo]
> %a{3}.push("bar")
[foo bar]
> %a{3}.push("baz")
[foo bar baz]
> .say for %a{3}
[foo bar baz]
> %a{3}.WHAT
(Array)
> .say for %a{3}.Array
foo
bar
baz
问题是 Scalar
容器执行 DWIM 间接寻址。
%a{3}
绑定到 Scalar
容器。
默认情况下,如果您引用 Scalar
容器的值或类型,您实际上访问的是值或值的类型,contained容器。
相比之下,当您将 Array
容器作为单个实体引用时,您确实访问了那个 Array
容器,没有花招。
要查看您 真正 处理的内容,请使用 .VAR
显示变量(或复合变量的元素)绑定到什么而不是允许它绑定到的任何容器都假装它不存在。
say %a{3}.VAR ; # $["foo", "bar", "baz"]
say %a{3}.Array.VAR ; # [foo bar baz]
这是一个匆忙的解释。我实际上正在研究 post 专门针对容器。
此处观察到的差异与以下相同:
my $a = [1,2,3];
.say for $a; # [1 2 3]
.say for $a.Array; # 1\n2\n3\n
$
印记可以被认为是 "a single item" 的意思。因此,当给 for
时,它会看到并说 "aha, a single item" 和 运行 循环一次。此行为在 for
和 运算符和例程 中是一致的。例如,这里是给定数组的 zip 运算符和它们的逐项数组:
say [1, 2, 3] Z [4, 5, 6]; # ((1 4) (2 5) (3 6))
say $[1, 2, 3] Z $[4, 5, 6]; # (([1 2 3] [4 5 6]))
相比之下,方法调用和索引操作 将始终在 Scalar
容器内部调用。对 .Array
的调用实际上是一个空操作,因为它已经在 Array
上被调用了,它有趣的工作实际上是方法调用本身的行为,它正在展开 Scalar
容器。 .WHAT
就像一个方法调用,它会告诉您任何 Scalar
容器内部的内容。
默认情况下,数组和散列的值是 Scalar
容器,这些容器依次保存值。然而,用于查看值的 .WHAT
隐藏了它,因为它是关于 Scalar
内部的内容。相比之下,.perl
[1] 清楚地表明有一个项目:
my Array %a;
%a{3}.push("foo");
%a{3}.push("bar");
say %a{3}.perl; $["foo", "bar"]
有多种方法可以删除分项:
%a{3}.Array # Identity minus the container
%a{3}.list # Also identity minus the container for Array
@(%a{3}) # Short for %a{3}.cache, which is same as .list for Array
%a{3}<> # The most explicit solution, using the de-itemize op
|%a{3} # Short for `%a{3}.Slip`; actually makes a Slip
在这种情况下我可能会使用 for %a{3}<> { }
;它既比方法调用更短,又清楚地表明我们这样做纯粹是为了删除项目而不是强制转换。
虽然 for |%a{3} { }
也可以很好地工作并且视觉效果很好,但它是唯一一个没有优化到简单地从其 Scalar
容器中删除某些东西,而是制作一个中间 Slip
对象,它可能会稍微减慢迭代速度(尽管取决于循环完成的工作量,这很可能是噪音)。
[1] 根据我写的,有人可能想知道为什么 .perl
可以恢复一些东西被逐项列出的事实。方法调用 $foo.bar
实际上是在做类似 $foo<>.^find_method('bar')($foo)
的事情。然后,在 method bar() { self }
中,self
绑定到调用该方法的对象,从其容器中移除。但是,可以编写 method bar(\raw-self:) { }
以完全按照提供的方式恢复它。
这是怎么回事?
如果 %a
有 Array
值而 %a{3}
是 Array
,为什么 %a{3}
和 %a{3}.Array
不同?
> my Array %a
{}
> %a{3}.push("foo")
[foo]
> %a{3}.push("bar")
[foo bar]
> %a{3}.push("baz")
[foo bar baz]
> .say for %a{3}
[foo bar baz]
> %a{3}.WHAT
(Array)
> .say for %a{3}.Array
foo
bar
baz
问题是 Scalar
容器执行 DWIM 间接寻址。
%a{3}
绑定到 Scalar
容器。
默认情况下,如果您引用 Scalar
容器的值或类型,您实际上访问的是值或值的类型,contained容器。
相比之下,当您将 Array
容器作为单个实体引用时,您确实访问了那个 Array
容器,没有花招。
要查看您 真正 处理的内容,请使用 .VAR
显示变量(或复合变量的元素)绑定到什么而不是允许它绑定到的任何容器都假装它不存在。
say %a{3}.VAR ; # $["foo", "bar", "baz"]
say %a{3}.Array.VAR ; # [foo bar baz]
这是一个匆忙的解释。我实际上正在研究 post 专门针对容器。
此处观察到的差异与以下相同:
my $a = [1,2,3];
.say for $a; # [1 2 3]
.say for $a.Array; # 1\n2\n3\n
$
印记可以被认为是 "a single item" 的意思。因此,当给 for
时,它会看到并说 "aha, a single item" 和 运行 循环一次。此行为在 for
和 运算符和例程 中是一致的。例如,这里是给定数组的 zip 运算符和它们的逐项数组:
say [1, 2, 3] Z [4, 5, 6]; # ((1 4) (2 5) (3 6))
say $[1, 2, 3] Z $[4, 5, 6]; # (([1 2 3] [4 5 6]))
相比之下,方法调用和索引操作 将始终在 Scalar
容器内部调用。对 .Array
的调用实际上是一个空操作,因为它已经在 Array
上被调用了,它有趣的工作实际上是方法调用本身的行为,它正在展开 Scalar
容器。 .WHAT
就像一个方法调用,它会告诉您任何 Scalar
容器内部的内容。
默认情况下,数组和散列的值是 Scalar
容器,这些容器依次保存值。然而,用于查看值的 .WHAT
隐藏了它,因为它是关于 Scalar
内部的内容。相比之下,.perl
[1] 清楚地表明有一个项目:
my Array %a;
%a{3}.push("foo");
%a{3}.push("bar");
say %a{3}.perl; $["foo", "bar"]
有多种方法可以删除分项:
%a{3}.Array # Identity minus the container
%a{3}.list # Also identity minus the container for Array
@(%a{3}) # Short for %a{3}.cache, which is same as .list for Array
%a{3}<> # The most explicit solution, using the de-itemize op
|%a{3} # Short for `%a{3}.Slip`; actually makes a Slip
在这种情况下我可能会使用 for %a{3}<> { }
;它既比方法调用更短,又清楚地表明我们这样做纯粹是为了删除项目而不是强制转换。
虽然 for |%a{3} { }
也可以很好地工作并且视觉效果很好,但它是唯一一个没有优化到简单地从其 Scalar
容器中删除某些东西,而是制作一个中间 Slip
对象,它可能会稍微减慢迭代速度(尽管取决于循环完成的工作量,这很可能是噪音)。
[1] 根据我写的,有人可能想知道为什么 .perl
可以恢复一些东西被逐项列出的事实。方法调用 $foo.bar
实际上是在做类似 $foo<>.^find_method('bar')($foo)
的事情。然后,在 method bar() { self }
中,self
绑定到调用该方法的对象,从其容器中移除。但是,可以编写 method bar(\raw-self:) { }
以完全按照提供的方式恢复它。