我有一个丑陋的调试功能,用于打印3级地图,实际上是他们的词典。我一直试图转换它,以便我可以打印出一张n级地图。这是我到目前为止的尝试:
open System.Text
open System.Collections.Generic
let rec PrintMap x =
let PrintVal (v: obj) = match v with
| :? seq<KeyValuePair<_,_>> as vm -> "{ " + PrintMap vm + " }"
| _ -> sprintf "Value: %s" (v.ToString())
let sb = StringBuilder()
for KeyValue(key,value) in x do
sb.AppendLine(sprintf "%s => %s" (key.ToString()) (PrintVal value)) |> ignore
sb.ToString()
type dict3 = Dictionary<string,obj>
type dict2 = Dictionary<string,dict3>
type dict1 = Dictionary<string,dict2>
let k1 = "k1"
let k2 = "k2"
let k3 = "k3"
let v = "foo" :> obj
let dict = dict1()
dict.[k1] <- dict2()
dict.[k1].[k2] <- dict3()
dict.[k1].[k2].[k3] <- v
printf "%s" (PrintMap dict)
我宁愿不把嵌套字典切换到他们的F#等价物(至少对于输入),因为真实的东西处理它们。
你可以推断出我几乎可以肯定的是对于活跃模式的天真尝试,甚至没有提到StringBuilder
,我对F#来说是一个新手。似乎我的困难在于说出字典和其他所有字母之间的区别。在查看该值时,通知类型推断系统,该值与&#39; x&#39;中的字典的类型不同。参数。
任何提示?
三江源!
一个小小的阐述---有时我会得到一个dict2
或dict3
类型而不是完整的dict1
,这就是我的原因之一试图使这更通用。
感谢Daniel:
let rec PrintMap x =
let PrintVal (v: obj) = match v with
| :? IDictionary as vd -> "{ " + PrintMap vd + " }"
| _ -> sprintf "%s" (v.ToString())
let sb = StringBuilder()
for key in x.Keys do
sb.AppendLine(sprintf "%s => %s" (key.ToString()) (PrintVal x.[key])) |> ignore
sb.ToString()
输出非常难看,但它确实有效。使用上面的输入字典:
k1 => { k2 => { k3 => foo
}
}
答案 0 :(得分:2)
在PrintVal
中,第二种情况从不匹配,因为seq<KeyValuePair<_,_>>
并不意味着任何键值对序列,而不管类型args ;它意味着将它留给编译器来推断类型args 。换句话说,下划线只是用于编译时类型推断的通配符,而不是模式匹配。在这种情况下,类型args可能推断为<obj, obj>
,它与测试中的任何字典都不匹配。
要取消此功能,您可能必须匹配非通用类型,例如System.Collections.IDictionary
。
答案 1 :(得分:0)
我认为你想做的事情是行不通的,而且理解F#并不是理解泛型。您必须记住,类型推断和泛型都是编译时规则,因此使用在运行时确定类型的对象调用此函数将永远不会起作用,因为编译器无法确定要应用的类型参数。换句话说,每次调用PrintMap v
时,编译器都必须知道v
的类型。
与C#中的类似问题一样,要解决此问题,您必须走dynamically invoking .NET methods using reflection的路线。
答案 2 :(得分:0)
虽然Daniel建议的解决方案适用于完整的词典,但它不能处理IDictionary<_,_>
的实现,就像F#dict
函数返回的那样。这可以通过测试接口并使用非泛型IEnumerable
来迭代其内容来实现:
let getProp name x =
x.GetType().InvokeMember(
name, BindingFlags.Public ||| BindingFlags.InvokeMethod |||
BindingFlags.Instance ||| BindingFlags.GetProperty, null, x, null )
let rec PrintMap x =
match x.GetType().GetInterface "IDictionary`2" with
| null -> sprintf "%A" x
| _ ->
let sb = StringBuilder()
sb.Append "{ " |> ignore
for o in unbox<System.Collections.IEnumerable> x do
let (k, v) = getProp "Key" o, getProp "Value" o
sb.AppendLine(sprintf "%s => %s" (string k) (PrintMap v)) |> ignore
sb.Append " }" |> ignore
string sb
dict["k1", dict["k2", dict["k3", "foo"]]]
|> PrintMap
输出(略有不同):
{ k1 => { k2 => { k3 => "foo"
}
}
}