辞書エントリの値を可変として宣言するにはどうすればよいですか?
-
05-07-2019 - |
質問
Googleは、F#辞書(またはその他のコレクション)のエントリを追加および削除する多くの例を提供します。しかし、私は同等のものの例を見ていない
myDict["Key"] = MyValue;
試しました
myDict.["Key"] <- MyValue
辞書を次のように宣言しようとしました
Dictionary<string, mutable string>
これに関するいくつかのバリエーションもあります。ただし、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)
エラーは次のとおりです:
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#には、2つの一般的な連想データ構造があります:
あなたが最も慣れているもの、それが継承する可変ディクショナリは、BCLに存在し、内部でハッシュテーブルを使用します。
let dict = new System.Collections.Generic.Dictionary<string,int>()
dict.["everything"] <- 42
もう一方はマップであり、一般的な機能スタイルで不変であり、バイナリツリーで実装されています。
ディクショナリを変更する操作の代わりに、マップは、要求された変更の結果である新しいマップを返す操作を提供します。多くの場合、内部ではマップ全体の完全に新しいコピーを作成する必要はないため、通常共有できる部分はそうです。例:
let withDouglasAdams = Map.add "everything" 42 Map.empty
値withDouglasAdams
は、<!> quot; everything <!> quot;の関連付けとして永久に残ります。 42までです。後で行う場合:
let soLong = Map.remove "everything" withDouglasAdams
この「削除」の効果は、soLong
値を介してのみ表示されます。
F#のマップは、前述のように、バイナリツリーとして実装されます。したがって、ルックアップはO(log n)であるのに対し、(正常に動作する)辞書はO(1)である必要があります。実際には、ハッシュベースのディクショナリは、一般的に使用されているように、ほとんどすべての単純な(要素数が少なく、衝突の可能性が低い)ツリーベースの辞書よりも優れている傾向があります。それは、マップの不変の側面により、辞書がより複雑なロックを必要とする状況で使用したり、副作用の少ないより「エレガントな」コードを書くことができるため、有用な代替手段であり続けることができるということです。
ただし、これは問題の原因ではありません。 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"))
...
他のヒント
どのようなエラーが発生しますか?私は以下を試してみましたが、うまくコンパイルされます
let map = new System.Collections.Generic.Dictionary<string,int>()
map.["foo"] <- 42
編集このコードも正常に実行されたことを確認します。