F#:在System.Collections.Dictionary中查找元素时出现奇怪的编译器错误

时间:2016-06-28 07:22:14

标签: dictionary f# c#-to-f#

我知道有更好的方法来解析F#中的JSON(比如FSharp.Data中的类型提供程序),但为了简单起见(因为我在fsx文件中执行此操作而且我没有'我还想处理Nuget + Paket用具),我在这里使用System.Web.Script.Serialization.JavaScriptSerializer

问题是当我尝试在字典中找到一个元素时,使用这个函数:

let isTestNet(): bool =
    let json = SomeFuncThatGivesMeAString()
    let jss = new JavaScriptSerializer()
    let dict = jss.DeserializeObject(json) :?> Dictionary<string, obj>
    for entry in dict do
        if (entry.Key.Equals("testnet")) then
            let result = entry.Value :?> bool
            result
    failwith "JSON response didn't include a 'testnet' element? " + json

编译器突出显示倒数第二行,但出现此错误:

error FS0001: This expression was expected to have type
    unit     
but here has type
    bool

这笔交易是什么?我甚至在函数头中指定了类型。为什么期望一个单位?

1 个答案:

答案 0 :(得分:3)

for表达式应评估为unit类型;将for结尾的 - unit表达式不会以某种方式使其成为封闭函数的返回值。最终你需要放弃for

一种选择是改为使用Seq.tryFind

let isTestNet () : bool =
    let dict =
        let json = (* ... *)
        let jss = JavaScriptSerializer()
        jss.DeserializeObject json :?> Dictionary<string, obj>
    match dict |> Seq.tryFind (fun entry -> entry.Key.Equals "testnet") with
      | Some entry -> entry.Value :?> bool
      | _ -> failwith ("JSON response didn't include a 'testnet' element? " + json)

(N.b。由于运算符优先级,错误消息的字符串连接必须括在括号中。)

虽然这很好,但Seq.tryFind会进行 O(N)搜索,而Dictionary本身会执行 O(1)搜索当直接使用时,如果字典是任何实际大小,这种方法是不可行的。

效率更高,稍微不那么明显(除了@ AntonSchwaighofer建议的改进):

let isTestNet () : bool =
    let dict =
        let json = (* ... *)
        let jss = JavaScriptSerializer()
        jss.DeserializeObject json :?> Dictionary<string, obj>
    match dict.TryGetValue "testnet" with
      | true, (:? bool as value) -> value
      | true, _ -> failwithf "'testnet' element was not a bool? %s" json
      | _ -> failwithf "JSON response didn't include a 'testnet' element? %s" json