F#:在 System.Collections.Dictionary 中查找元素时出现奇怪的编译器错误
F#: weird compiler error when finding an element in a System.Collections.Dictionary
我知道在 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
怎么回事?我什至在函数头中指定了类型。为什么它需要一个单位?
for
表达式的计算结果应为 unit
;让 for
以 non-unit
表达式结尾并不会以某种方式使其成为封闭函数的 return 值。最终你需要放弃 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
我知道在 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
怎么回事?我什至在函数头中指定了类型。为什么它需要一个单位?
for
表达式的计算结果应为 unit
;让 for
以 non-unit
表达式结尾并不会以某种方式使其成为封闭函数的 return 值。最终你需要放弃 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