Сортировка хеш-пар kv

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

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

Является ли %hash.sort({.key})>>.kv эквивалентом сортировки выше?

Почему эта сортировка не работает без гиперхинта >>?


person mpapec    schedule 31.07.2015    source источник


Ответы (2)


Метод sort возвращает Список из пар.

Поскольку вызов .kv в списке возвращает список из index, Pair списков, который вам не нужен; вы не можете просто вызвать .kv. Таким образом, вам нужно отдельно извлечь ключ и значение из объектов Pair в списке, вызвав метод .kv для каждого из них, что и делает >>.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 принимает все Вызывается там.

Я хотел бы отметить, что вам даже не нужно было указывать аргумент для sort, потому что это default еще умнее, чем то, что вы ему дали.

В 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
person Brad Gilbert    schedule 31.07.2015
comment
Вопрос сидит часами, и как раз когда я заканчиваю свой ответ, вы приходите и добавляете совершенно громоздкий! Но не волнуйтесь, все равно проголосуйте ;) - person Christoph; 31.07.2015
comment
@ Кристоф, я думал о том же. Ну ладно, не совсем то же самое, но все же. (Я собирался подождать, чтобы увидеть, за какой из них проголосовали первым / больше всего, прежде чем голосовать за ваш, но теперь я не вижу смысла ждать.) - person Brad Gilbert; 31.07.2015

Насколько я вижу, единственная разница между двумя версиями заключается в использовании блока с неявным параметром $_ вместо использования Whatever-Star, так что они действительно эквивалентны.

Это Perl, поэтому есть несколько способов сделать это:

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

Почему эта сортировка не работает без гиперхинта >>?

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"))

Вы можете принудительно вызвать Hash перед вызовом .kv:

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'";
}
person Christoph    schedule 31.07.2015
comment
for больше не сглаживается - person Brad Gilbert; 31.07.2015
comment
Это было недавнее изменение. В версии, которую я только что собрал из nom сегодня утром с помощью rakudobrew, запустив это: perl6 -e 'my %h = one => 1, two => 2, three => 3; for %h.sort(*.key)>>.kv { .say }', вы получите: one 1␤three 3␤two 2␤. Я на freenode.net#perl6, где обсуждалось изменение. - person Brad Gilbert; 31.07.2015
comment
@BradGilbert: исправлено; Время от времени я читаю журналы и действительно помню, как об этом рассказывали. - person Christoph; 31.07.2015
comment
не следить за развитием Perl6 несколько недель, и все изменится за вашей спиной; будем надеяться, что ответ, наконец, соответствует текущей семантике... - person Christoph; 31.07.2015