Comment déclarez-vous les valeurs d'une entrée de dictionnaire comme étant mutables?
-
05-07-2019 - |
Question
Google fournit de nombreux exemples d’ajout et de suppression d’entrées dans un dictionnaire F # (ou autre collection). Mais je ne vois pas d'exemples à l'équivalent de
myDict["Key"] = MyValue;
j'ai essayé
myDict.["Key"] <- MyValue
J'ai également tenté de déclarer le dictionnaire comme
Dictionary<string, mutable string>
aussi plusieurs variantes à ce sujet. Cependant, je n'ai pas encore trouvé la bonne combinaison ... si c'est réellement possible en F #.
Modifier: le code incriminé est:
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'erreur que je reçois est la suivante:
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
La solution
f # a deux structures de données associatives communes:
Celui auquel vous êtes le plus habitué, le dictionnaire mutable dont il hérite, c’est sa présence dans la BCL et qui utilise une table de hachage sous le capot.
let dict = new System.Collections.Generic.Dictionary<string,int>()
dict.["everything"] <- 42
L'autre est connu sous le nom Map et est, dans son style fonctionnel commun, immuable et implémentée avec des arbres binaires.
Au lieu d’opérations qui modifieraient un dictionnaire, les cartes fournissent des opérations qui renvoient une nouvelle carte, résultat de la modification demandée. Dans de nombreux cas, il n’est pas nécessaire de créer une copie entièrement nouvelle de l’ensemble de la carte. Les parties pouvant être partagées normalement le sont. Par exemple:
let withDouglasAdams = Map.add "everything" 42 Map.empty
La valeur withDouglasAdams
restera pour toujours comme une association de & "; tout &"; à 42. donc si vous faites plus tard:
let soLong = Map.remove "everything" withDouglasAdams
Ensuite, l'effet de cette "suppression" n'est visible que via la soLong
valeur.
La carte de F # est, comme mentionné, implémentée sous forme d'arborescence binaire. La recherche est donc O (log n) alors qu'un dictionnaire (bien comporté) devrait être O (1). En pratique, un dictionnaire à base de hachage aura tendance à surpasser celui à base d'arborescence dans la plupart des cas simples (faible nombre d'éléments, faible probabilité de collision) tel qu'il est couramment utilisé. Cela dit, l'aspect immuable de la carte peut vous permettre de l'utiliser dans des situations où le dictionnaire nécessiterait un verrouillage plus complexe ou d'écrire un code plus "élégant" avec moins d'effets secondaires, ce qui en fait une alternative utile.
Ce n’est cependant pas la source de votre problème. Le dict 'opérateur' retourne une implémentation explicitement immuable IDictionary<K,T>
(bien que cela ne soit pas indiqué dans sa documentation).
De fslib-extra-pervasives.fs (notez également l'utilisation des options sur les clés):
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"))
...
Autres conseils
Quelle erreur avez-vous? J'ai essayé ce qui suit et il compile très bien
let map = new System.Collections.Generic.Dictionary<string,int>()
map.["foo"] <- 42
MODIFIER : vérifiez que ce code fonctionne également correctement.