Come si dichiarano mutabili i valori di una voce del dizionario?
-
05-07-2019 - |
Domanda
Google offre molti esempi di aggiunta ed eliminazione di voci in un dizionario F # (o altra raccolta). Ma non vedo esempi all'equivalente di
myDict["Key"] = MyValue;
Ho provato
myDict.["Key"] <- MyValue
Ho anche tentato di dichiarare il dizionario come
Dictionary<string, mutable string>
e diverse varianti su questo. Tuttavia, non ho ancora raggiunto la combinazione corretta ... se è effettivamente possibile in F #.
Modifica: il codice offensivo è:
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)
L'errore che sto ricevendo è:
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
Soluzione
f # ha due strutture dati associative comuni:
Quello a cui sei più abituato, il Dizionario mutabile che eredita che è alla sua presenza nel BCL e usa una tabella hash sotto il cofano.
let dict = new System.Collections.Generic.Dictionary<string,int>()
dict.["everything"] <- 42
L'altro è noto come Mappa ed è, nello stile funzionale comune, immutabile e implementato con alberi binari.
Invece di operazioni che potrebbero cambiare un dizionario, le mappe forniscono operazioni che restituiscono una nuova mappa che è il risultato di qualsiasi modifica sia stata richiesta. In molti casi, sotto il cofano non è necessario creare una copia completamente nuova dell'intera mappa, quindi lo sono quelle parti che possono essere condivise normalmente. Ad esempio:
let withDouglasAdams = Map.add "everything" 42 Map.empty
Il valore withDouglasAdams
rimarrà per sempre come associazione di " tutto " a 42. quindi se in seguito lo fai:
let soLong = Map.remove "everything" withDouglasAdams
Quindi l'effetto di questa 'rimozione' è visibile solo tramite il soLong
valore.
La mappa di F # è, come detto, implementata come un albero binario. La ricerca è quindi O (log n) mentre un dizionario (ben educato) dovrebbe essere O (1). In pratica un dizionario basato sull'hash tenderà a sovraperformare quello basato sull'albero in quasi tutti i semplici (basso numero di elementi, bassa probabilità di collisione) in quanto tale è comunemente usato. Detto questo, l'aspetto immutabile della mappa può consentire di usarlo in situazioni in cui il dizionario richiederebbe invece un blocco più complesso o di scrivere un codice più "elegante" con meno effetti collaterali e quindi rimane un'alternativa utile.
Questa non è tuttavia la fonte del tuo problema. L'operatore 'dict restituisce un'implementazione IDictionary<K,T>
immutabile esplicitamente (nonostante non lo indichi nella sua documentazione).
Da fslib-extra-pervasives.fs (nota anche l'uso delle opzioni sui tasti):
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"))
...
Altri suggerimenti
Che errore ricevi? Ho provato quanto segue e si compila bene
let map = new System.Collections.Generic.Dictionary<string,int>()
map.["foo"] <- 42
MODIFICA Verifica che anche questo codice funzioni correttamente.