Почему: {} создает объект "Hash [Mu, Any]" (и что это такое и как он сравнивается с обычным "Hash")? - PullRequest
0 голосов
/ 27 декабря 2018

Сначала я просто удивился, почему перед пустыми скобками в строке 2 этого кода стоит двоеточие (из Календаря пришествия Perl 6, 25 декабря 2018 )?

sub is-happy( $n is copy ) {
    my $seen-numbers = :{};
    while $n > 1 {
        return False if $n ∈ $seen-numbers;
        $seen-numbers{$n} = True;
        $n = $n.comb.map(*²).sum
    }
    return True;
}

say is-happy(7);     # True
say is-happy(2018);  # False

Этот код запускается практически мгновенно.Я попытался say $seen-numbers.^name; и обнаружил, что это Hash[Mu,Any].

Однако, когда я удаляю двоеточие, is-happy(7) возвращает True, но затем is-happy(2018) связывает процессор в течение нескольких минут (покаЯ убил процесс).Кроме того, в этом случае say $seen-numbers.^name; печатает Hash.

Таким образом, очевидно, что :{} приводит к созданию Hash[Mu,Any].Как это работает?Это обходной путь или идиоматизм?А что такое Hash[Mu,Any] и как оно сравнивается с нормальным Hash?

1 Ответ

0 голосов
/ 27 декабря 2018

A Hash имеет Str клавиш.Любой ключ, который не является Str, будет приведен к одному до сохранения значения под этим строковым ключом.{} создает пустой Hash.

Это поведение можно изменить, указав аргументы типа Hash.Например, у Hash[Int] все еще есть строковый ключ с автоматическим приведением, но он может хранить только значения Int (так же, как Array[Int] может хранить только значения Int).Также можно передать аргумент второго типа, чтобы выбрать тип ключа.Это создает то, что часто называют «хэшем объекта», и в нем хранятся точные ключи, предоставленные в индексаторе.Таким образом, Hash[Mu,Any] - это хеш с неограниченными значениями и ключом Any (почти все типы подпадают под Any, за исключением Junction)..WHICH ключа используется для хеширования.

В большинстве случаев не создаются хэши объектов с использованием Hash[TValue,TKey], а вместо этого используется синтаксис объявления;например, я мог бы представить ребра в DAG, используя что-то вроде has Array %!edges-from{Mu}.Существует также способ создания хеша анонимного объекта, :{}, который вы наблюдаете в этом примере.

Итак, какая разница?Здесь:

$seen-numbers{$n} = True;

$n будет сохранен как Int, его .WHICH используется для хеширования для получения поиска O (1).Если бы мы просто использовали {} вместо этого, мы бы принуждали Int к ключу Str.Это различие становится важным здесь:

return False if $n ∈ $seen-numbers;

Поскольку оператор набора членства ищет значения, которые будут идентичны.Поэтому, когда у нас есть хеш объекта :{}, в котором хранятся Int с, тогда $n может быть идентичен одному из ключей в хеше;если это так, алгоритм завершается.Однако с обычным хэшем, созданным с помощью {}, ключи Str, поэтому никогда не идентичны Int, и поэтому проверка на членство в наборе всегда будет неудачной, что приведет к не прекращению работы алгоритма.

Программа соответствует собственным решениям.Можно написать:

return False if $seen-numbers{$n}:exists;

И тогда это будет работать и с {} (Hash).Тем не менее, он будет принудительно вводить числа в Str для хранения и поиска каждый раз, а стоимость :{} будет исключена.Таким образом, мы можем сказать, что программа сделала более эффективный выбор структуры данных, используя :{}, и это, в свою очередь, позволило использовать , что можно было бы считать более читабельным (особенно если целевая аудитория - люди, привыкшие кчитать такие математические операторы).

Еще один способ написать программу, которая, возможно, была более понятной:

my %seen-numbers is SetHash;
while $n > 1 {
    return False if $n ∈ %seen-numbers;
    %seen-numbers{$n} = True;
    $n = $n.comb.map(*²).sum
}
return True;

Что делает более ясным, что мы думаем о %seen-numbers как о наборе,хотя в этом случае название оставляет относительно небольшой вопрос о его назначении.

...