如何将字典条目的值声明为可变?

tel*_*re4 12 f# dictionary mutable

Google提供了大量在F#字典(或其他集合)中添加和删除条目的示例.但我没有看到相当于的例子

myDict["Key"] = MyValue;
Run Code Online (Sandbox Code Playgroud)

我试过了

myDict.["Key"] <- MyValue
Run Code Online (Sandbox Code Playgroud)

我也试图将词典声明为

Dictionary<string, mutable string>
Run Code Online (Sandbox Code Playgroud)

以及这方面的几个变种.然而,我都打不上的正确组合,但...如果它实际上可能在F#.

编辑:违规代码是:

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)
Run Code Online (Sandbox Code Playgroud)

我得到的错误是:

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
Run Code Online (Sandbox Code Playgroud)

Shu*_*oUk 27

f#有两个常见的关联数据结构:

你最习惯的那个,它继承的可变字典是它在BCL中的存在并且在引擎盖下使用哈希表.

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

另一个被称为Map,并且在通用功能样式中,是不可变的并且用二叉树实现.

而不是可以更改字典的操作,映射提供返回新映射的操作,该映射是请求的任何更改的结果.在许多情况下,在引擎盖下,不需要制作整个地图的全新副本,因此可以正常共享的那些部分是.例如:

let withDouglasAdams = Map.add "everything" 42 Map.empty
Run Code Online (Sandbox Code Playgroud)

该值withDouglasAdams将永远保留为"一切"与42的关联.所以如果您以后执行:

let soLong = Map.remove "everything" withDouglasAdams
Run Code Online (Sandbox Code Playgroud)

然后,这个'删除'的效果只能通过soLong值看到.

如上所述,F#的Map实现为二叉树.因此,查找是O(log n),而(表现良好的)字典应该是O(1).实际上,基于散列的字典在几乎所有简单(低数量的元素,低碰撞概率)中倾向于优于基于树的字典,因为这是常用的.也就是说,Map的不可变方面可能允许您在字典需要更复杂的锁定或编写更"优雅"的代码且副作用更少的情况下使用它,因此它仍然是一个有用的替代方案.

然而,这不是您的问题的根源.dict'operator'返回一个明确的不可变IDictionary<K,T>实现(尽管没有在它的文档中指出这一点).

fslib-extra-pervasives.fs(还要注意键上选项的使用):

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"))
   ...
Run Code Online (Sandbox Code Playgroud)

  • 请注意,二叉树中的查找是O(log n),而不是所述的O(n log n). (3认同)

Jar*_*Par 5

你得到什么错误?我尝试了以下,它编译得很好

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

编辑验证此代码运行良好.