Pergunta

O Google produz muito exemplo de adição e exclusão de entradas em um dicionário F# (ou outra coleção). Mas não vejo exemplos para o equivalente a

myDict["Key"] = MyValue;

eu tentei

myDict.["Key"] <- MyValue

Eu também tentei declarar o dicionário como

Dictionary<string, mutable string>

Além disso, várias variantes sobre isso. No entanto, ainda não atingi a combinação correta ... se for é realmente possível em f#.

Editar: o código ofensivo é:

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)

O erro que estou recebendo é:

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
Foi útil?

Solução

F# tem duas estruturas de dados associativas comuns:

Aquele que você está mais acostumado, o dicionário mutável que herda que é a presença na BCL e usa uma hashtable sob o capô.

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

O outro é conhecido como Mapa e é, no estilo funcional comum, imutável e implementado com árvores binárias.

Em vez de operações que mudariam um dicionário, os mapas fornecem operações que retornam um novo mapa, que é o resultado de qualquer alteração solicitada. Em muitos casos, sob o capô, não há necessidade de criar uma cópia totalmente nova de todo o mapa; portanto, as partes que podem ser compartilhadas normalmente são. Por exemplo:

let withDouglasAdams = Map.add "everything" 42 Map.empty

O valor que withDouglasAdams permanecerá para sempre como uma associação de "tudo" para 42. Então, se você mais tarde o fizer:

let soLong = Map.remove "everything" withDouglasAdams

Então o efeito dessa 'remoção' é apenas visível através do soLong valor.

O mapa de F#é, como mencionado, implementado como uma árvore binária. A pesquisa é, portanto, O (log n), enquanto um dicionário (bem comportado) deve ser O (1). Na prática, um dicionário baseado em hash tenderá a superar a árvore baseada em quase todos os simples (número baixo de elementos, baixa probabilidade de colisão), como tal é comumente usado. Dito isto, o aspecto imutável do mapa pode permitir que você o use em situações em que o dicionário exigiria bloqueio mais complexo ou escreveria mais código 'elegante' com menos efeitos colaterais e, portanto, continua sendo uma alternativa útil.

No entanto, essa não é a fonte do seu problema. O dicto 'Operador' retorna uma explicidade imutável IDictionary<K,T> implementação (apesar de não indicar isso em sua documentação).

A partir de fslib-extra-pervasives.fs (Observe também o uso de opções nas chaves):

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"))
   ...

Outras dicas

Que erro você recebe? Eu tentei o seguinte e ele compila muito bem

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

EDITAR Verifique se esse código também funcionou muito bem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top