如何将字典条目的值声明为可变的? [英] How do you declare the values of a dictionary entry as mutable?

查看:128
本文介绍了如何将字典条目的值声明为可变的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Google产生了许多在F#字典(或其他集合)中添加和删除条目的示例。但我没有看到相当于

  myDict [Key] = MyValue的示例; 

我尝试过

  myDict。[Key]<  -  MyValue 

我也有尝试将该词典声明为

 字典< string,mutable string> 

以及其中的几个变体。但是,我没有打正确的组合...如果它是实际上可能在F#。



编辑:违规代码是:

 键入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)

我得到的错误是:

  System.NotSupportedException:此值可能不会在Microsoft.FSharp.Core.ExtraTopLevelOperators.dict@37-2.set_Item(K key,V value)
中的
在< StartupCode $ FSI_0036> $ FSI_0036_Config。$ ctor @ 25-6.Invoke(Match mtch)
在Microsoft.FSharp.Collections.SeqModule.iter [T](FastFunc`2动作,IEnumerable`1序列)
在FSI_0036 .Utilities.Config .ctor(Option`1 fileName)
at< StartupCode $ FSI_0041>。$ FSI_0041.main @()
由于错误而停止


解决方案

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



你最常用的那个,它继承的可变字典,它在BCL中的存在,并在引擎盖下使用散列表。

 code> let dict = new System.Collections.Generic.Dictionary< string,int>()
dict。[everything]< - 42

另一个被称为映射,并且是通用的功能样式,是不可变的,并且用二叉树实现。



而不是将更改Dictionary的操作,map提供返回新映射的操作,这是所请求的任何更改的结果。在许多情况下,在引擎盖下,不需要制作整个地图的全新副本,所以可以正常共享的部分。例如:

  let withDouglasAdams = Map.addeverything42 Map.empty 

价值 withDouglasAdams 将永远保持为一切到42的关联。所以如果你以后做:

  let soLong = Map.removeeverythingwithDouglasAdams 
/ pre>

然后,这个删除的效果只有通过 soLong 值可见。



如上所述,F#的Map被实现为二叉树。因此,Lookup为O(log n),而(表现良好)的字典应为O(1)。实际上,通常使用的基于散列的字典将趋向于基于几乎所有简单(低数量的元素,低概率的碰撞)而以树为基础的字典。这就是说,地图的不变之处可能允许你在字典改为需要更加复杂的锁定或写更多的优雅代码的副作用较少的情况下使用它,因此它仍然是一个有用的替代方案。



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



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

  let dict l = 
//使用字典(这需要键类型上的哈希和等式)
//在Some(_)选项中包装键,以防它们为空
//(当System.Collections.Generic.Dictionary失败时)。悲伤但真实
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)
//给出字典的只读视图
{new IDictionary<'key,'a> ; with
member s.Item
with get x = d。[Some(x)]
and set(x,v)= raise(NotSupportedException(
this value may not被突变))
...


The Google yields plenty of example of adding and deleting entries in an F# dictionary (or other collection). But I don't see examples to the equivalent of

myDict["Key"] = MyValue;

I've tried

myDict.["Key"] <- MyValue

I have also attempted to declare the Dictionary as

Dictionary<string, mutable string>

as well several variants on this. However, I haven't hit on the correct combination yet... if it is actually possible in F#.

Edit: The offending code is:

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)

The error I'm getting is:

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

解决方案

f# has two common associative data structures:

The one you are most used to, the mutable Dictionary which it inherits that's to it's presence in the BCL and uses a hashtable under the hood.

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

The other is known as Map and is, in common functional style, immutable and implemented with binary trees.

Instead of operations that would change a Dictionary, maps provide operations which return a new map which is the result of whatever change was requested. In many cases, under the hood there is no need to make an entirely new copy of the entire map, so those parts that can be shared normally are. For example:

let withDouglasAdams = Map.add "everything" 42 Map.empty

The value withDouglasAdams will remain forever as an association of "everything" to 42. so if you later do:

let soLong = Map.remove "everything" withDouglasAdams

Then the effect of this 'removal' is only visible via the soLong value.

F#'s Map is, as mentioned, implemented as a binary tree. Lookup is therefore O(log n) whereas a (well behaved) dictionary should be O(1). In practice a hash based dictionary will tend to outperform the tree based one in almost all simple (low number of elements, low probability of collision) as such is commonly used. That said the immutable aspect of the Map may allow you to use it in situations where the dictionary would instead require more complex locking or to write more 'elegant' code with fewer side effects and thus it remains a useful alternative.

This is not however the source of your problem. The dict 'operator' returns an explicity immutable IDictionary<K,T> implementation (despite not indicating this in it's documentation).

From fslib-extra-pervasives.fs (note also the use of options on the keys):

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"))
   ...

这篇关于如何将字典条目的值声明为可变的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆