¿Cómo declaras los valores de una entrada de diccionario como mutables?
-
05-07-2019 - |
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
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
.
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.