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

StackOverflow https://stackoverflow.com/questions/1203209

  •  05-07-2019
  •  | 
  •  

Вопрос

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 Microsoft.FSharp.Core.ExtraTopLevelOperators.dict@37-2.set_Item(K key, V value)
   at <StartupCode$FSI_0036>.$FSI_0036_Config.$ctor@25-6.Invoke(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>.$FSI_0041.main@()
stopped due to error
Это было полезно?

Решение

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 #, как уже упоминалось, реализована в виде двоичного дерева. Следовательно, Lookup - это O (log n), тогда как словарь (с хорошим поведением) должен быть O (1). На практике словарь, основанный на хеше, будет стремиться превзойти древовидный словарь почти во всех простых (малое количество элементов, низкая вероятность столкновения), так как он обычно используется. При этом неизменный аспект Map может позволить вам использовать его в ситуациях, когда словарь вместо этого потребует более сложной блокировки или для написания более «элегантного» кода с меньшим количеством побочных эффектов, и, таким образом, он остается полезной альтернативой.

Однако это не источник вашей проблемы. Диктовка '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"))
   ...

Другие советы

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top