Как вы объявляете значения словаря в качестве изменяемого?

Google дает множество примеров добавления и удаления записей в словаре F # (или другой коллекции). Но я не вижу примеров эквивалента

myDict["Key"] = MyValue;

Я пробовал

myDict.["Key"] <- MyValue

Я также попытался объявить словарь как

Dictionary<string, mutable string>

а также несколько вариантов этого. Тем не менее, я еще не попал в правильную комбинацию... если это действительно возможно в F #.

Изменить: Код нарушения:

type Config(?fileName : string) =
    let fileName = defaultArg fileName @"C:\path\myConfigs.ini"

    static let settings =
        dict[ "Setting1", "1";
              "Setting2", "2";
              "Debug",    "0";
              "State",    "Disarray";]

    let settingRegex = new Regex(@"\s*(?<key>([^;#=]*[^;#= ]))\s*=\s*(?<value>([^;#]*[^;# ]))")

    do  File.ReadAllLines(fileName)
        |> Seq.map(fun line -> settingRegex.Match(line))
        |> Seq.filter(fun mtch -> mtch.Success)
        |> Seq.iter(fun mtch -> settings.[mtch.Groups.Item("key").Value] <- mtch.Groups.Item("value").Value)

Ошибка, которую я получаю:

System.NotSupportedException: This value may not be mutated
   at [email protected]_Item(K key, V value)
   at <StartupCode$FSI_0036>[email protected](Match mtch)
   at Microsoft.FSharp.Collections.SeqModule.iter[T](FastFunc`2 action, IEnumerable`1 sequence)
   at FSI_0036.Utilities.Config..ctor(Option`1 fileName)
   at <StartupCode$FSI_0041>[email protected]()
stopped due to error

Ответы

Ответ 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"))
   ...

Ответ 2

Какую ошибку вы получаете? Я пробовал следующее, и он просто компилируется

let map = new System.Collections.Generic.Dictionary<string,int>()
map.["foo"] <- 42

РЕДАКТИРОВАТЬ Убедитесь, что этот код также работает очень хорошо.