Ответ 1
f # имеет две общие ассоциативные структуры данных:
То, к которому вы больше всего привыкли, измененный словарь, который он наследует, чтобы он присутствовал в BCL, и использует хэш-таблицу под капотом.
let dict = new System.Collections.Generic.Dictionary<string,int>()
dict.["everything"] <- 42
Другой известен как Map и, в общем функциональном стиле, неизменен и реализован с бинарными деревьями.
Вместо операций, которые изменяют словарь, карты предоставляют операции, которые возвращают новую карту, которая является результатом любого изменения. Во многих случаях под капотом нет необходимости делать совершенно новую копию всей карты, поэтому те части, которые могут быть разделены, как правило, являются. Например:
let withDouglasAdams = Map.add "everything" 42 Map.empty
Значение withDouglasAdams
останется навсегда как ассоциация "всего" до 42. так что если вы позже выполните:
let soLong = Map.remove "everything" withDouglasAdams
Тогда эффект этого "удаления" отображается только с помощью значения soLong
.
F # Map, как уже упоминалось, реализован как двоичное дерево. Таким образом, Lookup - это O (log n), тогда как словарь (хорошо выполненный) должен быть O (1). На практике словарь, основанный на хеше, будет стремиться превзойти дерево, основанное на почти всех простых (низкое количество элементов, низкая вероятность столкновения), как таковое обычно используется. Тем не менее неизменный аспект Карты может позволить вам использовать ее в ситуациях, когда словарь вместо этого требует более сложной блокировки или для написания более "элегантного" кода с меньшим количеством побочных эффектов, и, таким образом, он остается полезной альтернативой.
Это, однако, не является источником вашей проблемы. Оператор dict 'возвращает неизменную экспликацию IDictionary<K,T>
(несмотря на то, что это не указывает на это в документации).
Из fslib-extra-pervasives.fs(обратите внимание также на использование опций на клавишах):
let dict l =
// Use a dictionary (this requires hashing and equality on the key type)
// Wrap keys in an Some(_) option in case they are null
// (when System.Collections.Generic.Dictionary fails). Sad but true.
let t = new Dictionary<Option<_>,_>(HashIdentity.Structural)
for (k,v) in l do
t.[Some(k)] <- v
let d = (t :> IDictionary<_,_>)
let c = (t :> ICollection<_>)
let ieg = (t :> IEnumerable<_>)
let ie = (t :> System.Collections.IEnumerable)
// Give a read-only view of the dictionary
{ new IDictionary<'key, 'a> with
member s.Item
with get x = d.[Some(x)]
and set (x,v) = raise (NotSupportedException(
"This value may not be mutated"))
...