【问题标题】:Sorting hash kv pairs对哈希 kv 对进行排序
【发布时间】:2015-10-23 02:25:26
【问题描述】:
my %hash =
    two   => 2,
    three => 3,
    one   => 1,
;

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

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

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

【问题讨论】:

    标签: sorting hashmap raku


    【解决方案1】:

    sort 方法返回 List 的 Pairs。

    由于在列表上调用.kv 会返回一个index, Pair 列表列表,这是您不想要的;你不能只打电话给.kv。因此,您必须通过在每个对象上调用.kv 方法,从列表中的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'";
    }
    

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

    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}),是的,这基本上是一样的,因为sort在那里接受任何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
    

    【讨论】:

    • 这个问题持续了几个小时,就在我最终确定答案的时候,你过来添加一个完美的答案!但不用担心,无论如何都要投赞成票;)
    • @Christoph 我也在想同样的事情。好吧,不完全一样,但仍然。 (我本来打算等着看哪一个得到第一个/最多的,然后再给你点赞,但现在我看不出等待的意义。)
    • my %next-prime = 9 =&gt; 3, 12 =&gt; 5, 5 =&gt; 7, 3 =&gt; 11, 11 =&gt; 13; for %next-prime.sort -&gt; (:$key, :$value) { say "'$key' =&gt; '$value'"; } 给出键未完全排序的输出。好像单数和双数是分开排序的。
    • @SumanKhanal 原因是%next-prime 有基于字符串的键,所以它当然会按字符串顺序而不是数字顺序对它们进行排序。你要么需要告诉$next-prime它正在使用数字my $next-prime is Hash[Int,Int] =,或者你需要告诉sort使用数字%next-prime.sort( +*.key )
    • @BradGilbert my %next-prime is Hash[Int,Int] = 运行良好,但 my $next-prime is Hash[Int,Int] = 没有。谢谢你的提示。
    【解决方案2】:

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

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

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

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

    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 来删除它们。如果这样做,您可以使用 -&gt; $key, $value 作为 for 循环的签名,而不是使用签名绑定来解包包裹的 -&gt; ($key, $value)(或更明确地说,-&gt; $anon ($key, $value))。

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

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

    【讨论】:

    • for 不再变平
    • 这是最近的变化。在我今天早上使用 rakudobrew 从 nom 构建的版本上,运行:perl6 -e 'my %h = one =&gt; 1, two =&gt; 2, three =&gt; 3; for %h.sort(*.key)&gt;&gt;.kv { .say }' 生成:one 1␤three 3␤two 2␤。我在 freenode.net#perl6 上,这是讨论更改的地方。
    • @BradGilbert:已修复;我偶尔会阅读日志,并且确实记得一些关于此的自行车事故
    • 好几个星期都不要跟踪 Perl6 的开发,一切都会在你背后发生变化;让我们希望答案最终符合当前的语义......
    猜你喜欢
    • 2015-01-01
    • 1970-01-01
    • 2019-04-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-16
    • 2012-01-21
    • 2013-12-04
    相关资源
    最近更新 更多