对聚合中包含的列表感到困惑,也许是上下文问题?
confusion about lists contained in an aggregate, maybe context problem?
Rakudo 版本 2020.01
我正在写一些一次性代码,并没有费心去实现一个 class,只是使用了一个 Hash 作为类似的工作。我发现列表有一些令人惊讶的行为。
class Q1 {}
class R1 {
has Str $.some-str is required;
has @.some-list is required;
}
my $r1 = R1.new(
some-str => '…',
some-list => (Q1.new, Q1.new, Q1.new)
);
# hash as poor man's class
my $r2 = {
some-str => '…',
some-list => (Q1.new, Q1.new, Q1.new)
};
multi sub frob(R1 $r1) {
for #`(Array) $r1.some-list -> $elem {
$elem.raku.say;
}
}
multi sub frob(Hash $r2) {
for #`(List) $r2<some-list> -> $elem {
$elem.raku.say;
}
}
frob $r1;
# OK.
# Q1.new
# Q1.new
# Q1.new
frob $r2;
# got:
# (Q1.new, Q1.new, Q1.new)
# expected:
# Q1.new
# Q1.new
# Q1.new
当我在列表中调用 .flat
或 .list
时,frob(Hash …)
按预期工作(即使它已经是一个列表‽)。
我试图制作一个最小的测试用例,但这与 AFAICT 的工作原理相同。
for [Q1.new, Q1.new, Q1.new] -> $elem {
$elem.raku.say;
}
for (Q1.new, Q1.new, Q1.new) -> $elem {
$elem.raku.say;
}
List 和 Scalar 的文档我看了好几遍,但我还是无法理解。为什么我必须特殊对待 Hash 中的列表,而不是 class?
这与 []
与 ()
无关。这与$
(表示一个项目)和%
(表示一个关联词)之间的区别有关:
sub a(%h) { dd %h } # a sub taking an Associative
sub b(Hash $h) { dd $h } # a sub taking an item of type Hash
a { a => 42 }; # Hash % = {:a(42)}
b { a => 42 }; # ${:a(42)}
在"b"情况下,收到的是一个项目。如果您尝试对其进行迭代,您将获得该项目的 1 次迭代。而在 "a" 的情况下,您已经指出它是您想要的 Associative 东西(带有 %
印记)。
也许更清楚的例子:
my $a = (1,2,3);
for $a { dd $_ } # List $a = $(1, 2, 3)
因为 $a
是一个项目,你得到一个迭代。您可以通过添加 .list
:
来表明您想要迭代基础事物
for $a.list { dd $_ } # 123
或者,如果你想得到更多的线条噪音,前缀 a @
:
for @$a { dd $_ } # 123
严格来说不是答案,而是观察:在 Raku 中,使用 类 而不是散列是值得的,这与 Perl 相反:
my %h = a => 42, b => 666;
for ^10000000 { my $a = %h<a> }
say now - INIT now; # 0.4434793
使用 类 和对象:
class A { has $.a; has $.b }
my $h = A.new(a => 42, b => 666);
for ^10000000 { my $a = $h.a }
say now - INIT now; # 0.368659
不仅使用 类 速度更快,如果添加 is required
特性,它还可以防止您在初始化时出现拼写错误:
class A { has $.a is required; has $.b is required }
A.new(a => 42, B => 666);
# The attribute '$!b' is required, but you did not provide a value for it.
并且它可以防止您在访问时出现错别字:
my $a = A.new(a => 42, b => 666);
$a.bb;
# No such method 'bb' for invocant of type 'A'. Did you mean 'b'?
for
不会遍历逐项值。
当您将某物放入标量容器中时,它会被逐项列出。
sub foo ( $v ) { # itemized
for $v { .say }
}
sub bar ( \v ) {
for v { .say }
}
foo (1,2,3);
# (1 2 3)
bar (1,2,3);
# 1
# 2
# 3
哈希中的元素也是标量容器。
my %h = 'foo' => 'bar';
say %h<foo>.VAR.^name;
# Scalar
因此,如果您将列表放入哈希中,它将逐项列出。
my %h;
my \list = (1,2,3);
%h<list> = list;
say list.VAR.^name;
# List
say %h<list>.VAR.^name;
# Scalar
因此,如果您想遍历这些值,则必须对其进行逐项化处理。
%h<list>[]
%h<list><>
%h<list>.list
%h<list>.self
@(%h<list>)
given %h<list> -> @list { … }
my @list := %h<list>;
(my @ := %h<list>) # inline version of previous example
您可以通过绑定来避免这个标量容器。
%h<list> := list;
(这会阻止 =
运算符处理该散列元素。)
如果您注意到在 class 对象中您用 @
而非 $
定义了它
class R1 {
has Str $.some-str is required;
has @.some-list is required;
}
如果您将其更改为 $
并将其标记为 rw
,它将像哈希示例一样工作
class R2 {
has Str $.some-str is required;
has List $.some-list is required is rw;
}
my $r2 = R2.new(
some-str => '…',
some-list => (1,2,3),
);
for $r2.some-list { .say }
# (1 2 3)
它必须是一个 $
变量,否则它不会在标量容器中。
它还必须标记为 rw
以便访问器 returns 实际的标量容器而不是逐项化的值。
Rakudo 版本 2020.01
我正在写一些一次性代码,并没有费心去实现一个 class,只是使用了一个 Hash 作为类似的工作。我发现列表有一些令人惊讶的行为。
class Q1 {}
class R1 {
has Str $.some-str is required;
has @.some-list is required;
}
my $r1 = R1.new(
some-str => '…',
some-list => (Q1.new, Q1.new, Q1.new)
);
# hash as poor man's class
my $r2 = {
some-str => '…',
some-list => (Q1.new, Q1.new, Q1.new)
};
multi sub frob(R1 $r1) {
for #`(Array) $r1.some-list -> $elem {
$elem.raku.say;
}
}
multi sub frob(Hash $r2) {
for #`(List) $r2<some-list> -> $elem {
$elem.raku.say;
}
}
frob $r1;
# OK.
# Q1.new
# Q1.new
# Q1.new
frob $r2;
# got:
# (Q1.new, Q1.new, Q1.new)
# expected:
# Q1.new
# Q1.new
# Q1.new
当我在列表中调用
.flat
或 .list
时,frob(Hash …)
按预期工作(即使它已经是一个列表‽)。
我试图制作一个最小的测试用例,但这与 AFAICT 的工作原理相同。
for [Q1.new, Q1.new, Q1.new] -> $elem {
$elem.raku.say;
}
for (Q1.new, Q1.new, Q1.new) -> $elem {
$elem.raku.say;
}
List 和 Scalar 的文档我看了好几遍,但我还是无法理解。为什么我必须特殊对待 Hash 中的列表,而不是 class?
这与 []
与 ()
无关。这与$
(表示一个项目)和%
(表示一个关联词)之间的区别有关:
sub a(%h) { dd %h } # a sub taking an Associative
sub b(Hash $h) { dd $h } # a sub taking an item of type Hash
a { a => 42 }; # Hash % = {:a(42)}
b { a => 42 }; # ${:a(42)}
在"b"情况下,收到的是一个项目。如果您尝试对其进行迭代,您将获得该项目的 1 次迭代。而在 "a" 的情况下,您已经指出它是您想要的 Associative 东西(带有 %
印记)。
也许更清楚的例子:
my $a = (1,2,3);
for $a { dd $_ } # List $a = $(1, 2, 3)
因为 $a
是一个项目,你得到一个迭代。您可以通过添加 .list
:
for $a.list { dd $_ } # 123
或者,如果你想得到更多的线条噪音,前缀 a @
:
for @$a { dd $_ } # 123
严格来说不是答案,而是观察:在 Raku 中,使用 类 而不是散列是值得的,这与 Perl 相反:
my %h = a => 42, b => 666;
for ^10000000 { my $a = %h<a> }
say now - INIT now; # 0.4434793
使用 类 和对象:
class A { has $.a; has $.b }
my $h = A.new(a => 42, b => 666);
for ^10000000 { my $a = $h.a }
say now - INIT now; # 0.368659
不仅使用 类 速度更快,如果添加 is required
特性,它还可以防止您在初始化时出现拼写错误:
class A { has $.a is required; has $.b is required }
A.new(a => 42, B => 666);
# The attribute '$!b' is required, but you did not provide a value for it.
并且它可以防止您在访问时出现错别字:
my $a = A.new(a => 42, b => 666);
$a.bb;
# No such method 'bb' for invocant of type 'A'. Did you mean 'b'?
for
不会遍历逐项值。
当您将某物放入标量容器中时,它会被逐项列出。
sub foo ( $v ) { # itemized
for $v { .say }
}
sub bar ( \v ) {
for v { .say }
}
foo (1,2,3);
# (1 2 3)
bar (1,2,3);
# 1
# 2
# 3
哈希中的元素也是标量容器。
my %h = 'foo' => 'bar';
say %h<foo>.VAR.^name;
# Scalar
因此,如果您将列表放入哈希中,它将逐项列出。
my %h;
my \list = (1,2,3);
%h<list> = list;
say list.VAR.^name;
# List
say %h<list>.VAR.^name;
# Scalar
因此,如果您想遍历这些值,则必须对其进行逐项化处理。
%h<list>[]
%h<list><>
%h<list>.list
%h<list>.self
@(%h<list>)
given %h<list> -> @list { … }
my @list := %h<list>;
(my @ := %h<list>) # inline version of previous example
您可以通过绑定来避免这个标量容器。
%h<list> := list;
(这会阻止 =
运算符处理该散列元素。)
如果您注意到在 class 对象中您用 @
而非 $
class R1 {
has Str $.some-str is required;
has @.some-list is required;
}
如果您将其更改为 $
并将其标记为 rw
,它将像哈希示例一样工作
class R2 {
has Str $.some-str is required;
has List $.some-list is required is rw;
}
my $r2 = R2.new(
some-str => '…',
some-list => (1,2,3),
);
for $r2.some-list { .say }
# (1 2 3)
它必须是一个 $
变量,否则它不会在标量容器中。
它还必须标记为 rw
以便访问器 returns 实际的标量容器而不是逐项化的值。