Ответ 1
Hash
имеет ключи Str
. Любой ключ, который не является Str
будет приведен к одному перед сохранением значения под этим строковым ключом. {}
Создает пустой Hash
.
Это поведение можно изменить, 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
как о наборе, хотя в этом случае имя оставляет относительно небольшой вопрос о его назначении.