排序哈希 kv 对

Sorting hash kv pairs

my %hash =
    two   => 2,
    three => 3,
    one   => 1,
;

for %hash.sort(*.key)>>.kv -> ($key, $value) {
    say "'$key' => '$value'";
}

%hash.sort({.key})>>.kv是否等同于上面的排序?

如果没有超级 >> 提示,为什么这种排序不起作用?

sort method is returning a List of Pairs.

由于通过对每个对象调用 .kv 方法来单独调用列表中的 .kv on the list returns a list of index, Pair lists, which you don't want; you can't just call .kv. So you have to pull out the key and value from the Pair 个对象,>>.kv 确实如此。

您也可以使用 .map(*.kv)

>>.kv 语法允许实现将工作分散到多个线程(如果这样做有意义)。
(目前 Rakudo 只是以半随机顺序进行工作,以防止人们错误地使用该功能)


还有另一种编写循环的方法,即在子签名中使用副词提取属性:

for %hash.sort -> (:$key, :$value) {
  say "'$key' => '$value'";
}

for %hash.sort -> $pair (:$key, :$value) {
  say $pair;
  say $key === $pair.key and $value === $pair.value; # True␤
}

# :$key is short for :key($key)
for %hash.sort -> (:key($k), :value($v)) {
  say "'$k' => '$v'";
}

这对于没有创建其 public 属性列表的方法的其他对象很有用

class C { has $.a; has $.b; has $.c; has $!private-value }
my $c = 5;
my $obj = C.new(:a<A>,:b(1),:$c);

given $obj -> ( :$a, :b($b), :$c) ) {
  say "$a $b $c";
}

# ignore $.a by using an unnamed scalar
given $obj -> ( :a($), :$b, :$c ) { ... }

# places any unspecified public attributes in %others
given $obj -> ( :$a, :$b, *%others ) {
  .say for keys %others; # c␤
}

# ignores any unspecified attributes
# useful to allow subclasses to add more attributes
# or to just drop any values you don't care about
given $obj -> ( :$a, :$b, *% ) { ... }

# fails because it doesn't handle the public c attribute
# in the sub-signature
given $obj -> ( :$a, :$b ) { ... }

这只是签名可能的开始。

子例程和方法签名中也允许以下所有内容,可选的,并且对于此示例来说完全是多余的。 它在限制可能的候选人的多子和多方法中非常有用。

for 'one' => 1, 1/3
->
  # Type is an alias to the object type
  ::Type Any $_ # Any is the default type requirement

  # the public attributes of the object
  (
    ::A-Type Any :key(   :numerator(   $a ) ),
    ::B-Type Any :value( :denominator( $b ) ) where $b >= 1,
  )
{
  my Type $obj = $_; # new variable declared as having the same type
  my A-Type $new-a = $a;
  my B-Type $new-b = $b;

  # could have used $_.^name or .^name instead of Type.^name
  # so you don't actually have to add the alias to the signature
  # to get the name of the arguments type
  say Type.^name, ' ', $_;
  say '  ', A-Type.^name, ' ', $a;
  say '  ', B-Type.^name, ' ', $b;
}
Pair one => 1
  Str one
  Int 1
Rat 0.333333
  Int 1
  Int 3

至于在那里使用.sort({.key}), yes that is basically the same thing, as sort accepts anything Callable

我想指出,您甚至不需要为 sort 提供参数,因为它的默认设置甚至比您提供的更聪明。

Perl 6 有很多创建和访问 Callable 东西的方法。因此,以下任何一项都有效:

*.key
{ .key } # { $_.key }
-> $_ { .key } # basically what the previous line turns into
{ $^placeholder-var.key }
sub ($_) { .key }
&a-subroutine-reference # you would have to create the subroutine though

此外,由于所有普通运算符实际上都是子例程,因此您可以在其他需要 Callable 的地方使用它们。 (虽然我想不出那个地方可以用)

&infix:<+> # the subroutines responsible for the numeric addition operator
&[+] # ditto

&prefix:<++>
&postfix:<++>

# etc

据我所知,这两个版本之间的唯一区别是使用带有隐式 $_ 参数的块而不是使用 Whatever-Star,因此它们确实是等价的。

这是 Perl,所以有不止一种方法:

*.key
{ .key }
{ $^arg.key }
-> $arg { $arg.key }

Why this sort doesn't work without hyper >> hint?

sort 将散列强制转换成一对列表,这就是您将得到的:

say %hash.sort(*.key).perl;
# ("one" => "1", "three" => "3", "two" => "2")

要删除对,您需要遍历列表并对每个对调用 .kv

say %hash.sort(*.key)>>.kv.perl;
# (("one", "1"), ("three", "3"), ("two", "2"))

say %hash.sort(*.key).map(*.kv).perl;
# (("one", "1"), ("three", "3"), ("two", "2"))

您可以在调用 .kv 之前强制 Hash:

say %hash.sort(*.key).hash.kv.perl;
# ("one", "1", "three", "3", "two", "2")

但这当然会破坏练习的目的,因为不能依赖哈希排序。

您可能已经注意到,根据您编写代码的准确程度,您会得到不同的结果。如果没有尾随.list,你得到的实际上是一个Parcel而不是一个List,但是语义还没有最终确定。

请注意,即使返回的对象都用简单的括号表示,但有些是包裹,有些是列表,您可以通过调用 .WHAT 来检查。这项工作仍在进行中。

另请注意其中一些变体中的内括号,您可以通过调用 .flat 将其删除。如果这样做,您可以使用 -> $key, $value 作为 for 循环的签名,而不是 -> ($key, $value)(或者更明确地说,-> $anon ($key, $value)),它使用签名绑定来解压包裹。

除了使用 .kv,您还可以使用相同的方法来解压缩对象对:

for %hash.sort(*.key) -> (:$key, :$value) {
    say "'$key' => '$value'";
}