Comment déclarez-vous les valeurs d'une entrée de dictionnaire comme étant mutables?

StackOverflow https://stackoverflow.com/questions/1203209

  •  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
Était-ce utile?

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.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top