Pregunta

Google ofrece muchos ejemplos de agregar y eliminar entradas en un diccionario F # (u otra colección). Pero no veo ejemplos para el equivalente de

myDict["Key"] = MyValue;

Lo he intentado

myDict.["Key"] <- MyValue

También he intentado declarar el Diccionario como

Dictionary<string, mutable string>

así como varias variantes sobre esto. Sin embargo, todavía no he encontrado la combinación correcta ... si es realmente posible en F #.

Editar: El código ofensivo es:

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)

El error que obtengo es:

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
¿Fue útil?

Solución

f # tiene dos estructuras de datos asociativas comunes:

Al que estás más acostumbrado, el Diccionario mutable que hereda es su presencia en el BCL y usa una tabla hash debajo del capó.

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

El otro se conoce como Mapa y es, en un estilo funcional común, inmutable e implementado con árboles binarios.

En lugar de operaciones que cambiarían un Diccionario, los mapas proporcionan operaciones que devuelven un nuevo mapa que es el resultado de cualquier cambio solicitado. En muchos casos, debajo del capó no hay necesidad de hacer una copia completamente nueva de todo el mapa, por lo que las partes que se pueden compartir normalmente lo son. Por ejemplo:

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

El valor withDouglasAdams permanecerá para siempre como una asociación de & "; todo &"; a 42. así que si luego lo haces:

let soLong = Map.remove "everything" withDouglasAdams

Entonces el efecto de esta 'eliminación' solo es visible a través del valor soLong.

El mapa de

F # se implementa, como se mencionó, como un árbol binario. Por lo tanto, la búsqueda es O (log n), mientras que un diccionario (bien comportado) debe ser O (1). En la práctica, un diccionario basado en hash tenderá a superar al basado en árbol en casi todos los simples (bajo número de elementos, baja probabilidad de colisión) como tal se usa comúnmente. Dicho esto, el aspecto inmutable del Mapa puede permitirle usarlo en situaciones en las que el diccionario requeriría un bloqueo más complejo o escribir código más 'elegante' con menos efectos secundarios y, por lo tanto, sigue siendo una alternativa útil.

Sin embargo, esta no es la fuente de su problema. El 'operador' dict devuelve una implementación IDictionary<K,T> inmutable explícitamente (a pesar de no indicar esto en su documentación).

De fslib-extra-pervasives.fs (tenga en cuenta también el uso de opciones en las teclas):

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

Otros consejos

¿Qué error obtienes? Intenté lo siguiente y se compila muy bien

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

EDITAR Verifique que este código también funcionó bien.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top