f # имеет две общие ассоциативные структуры данных:
Тот, к которому вы больше всего привыкли, изменяемый словарь, который он наследует, который находится в BCL и использует хеш-таблицу под капотом.
let dict = new System.Collections.Generic.Dictionary<string,int>()
dict.["everything"] <- 42
Другой известен как Карта и в обычном функциональном стиле неизменен и реализован с помощью двоичных деревьев.
Вместо операций, которые могли бы изменить словарь, карты предоставляют операции, которые возвращают новую карту, которая является результатом любого изменения, которое было запрошено. Во многих случаях под капотом нет необходимости делать полностью новую копию всей карты, поэтому обычно доступны те части, которые могут быть общими. Например:
let withDouglasAdams = Map.add "everything" 42 Map.empty
Значение withDouglasAdams
останется навсегда как ассоциация «всего» с 42. так что если вы позже сделаете:
let soLong = Map.remove "everything" withDouglasAdams
Тогда эффект этого «удаления» виден только через значение soLong
.
Карта F #, как уже упоминалось, реализована в виде двоичного дерева. Следовательно, Lookup - это O (log n), тогда как словарь (с хорошим поведением) должен быть O (1). На практике словарь, основанный на хеше, будет стремиться превзойти древовидный словарь почти во всех простых (малое количество элементов, низкая вероятность столкновения), так как он обычно используется. При этом неизменный аспект Map может позволить вам использовать его в ситуациях, когда словарь вместо этого потребует более сложной блокировки или для написания более «элегантного» кода с меньшим количеством побочных эффектов, и, таким образом, он остается полезной альтернативой.
Это, однако, не источник вашей проблемы. Параметр dict 'operator' возвращает неизменную реализацию 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"))
...