阅读开始F# -罗伯特·皮克林我专注于以下内容:
程序员从OCaml的背景来车时应当使用F#异常时要小心。 由于CLR的体系结构,抛出异常是相当昂贵的,相当比OCaml的更贵一点。 如果你扔了很多异常,仔细分析代码来决定性能的成本是否值得。 如果成本过高,适当地修改代码。
为什么呢,因为CLR的,抛出异常是比较昂贵的,如果F#
比OCaml
? 什么是在这种情况下,适当地修改代码的最佳方式?
阅读开始F# -罗伯特·皮克林我专注于以下内容:
程序员从OCaml的背景来车时应当使用F#异常时要小心。 由于CLR的体系结构,抛出异常是相当昂贵的,相当比OCaml的更贵一点。 如果你扔了很多异常,仔细分析代码来决定性能的成本是否值得。 如果成本过高,适当地修改代码。
为什么呢,因为CLR的,抛出异常是比较昂贵的,如果F#
比OCaml
? 什么是在这种情况下,适当地修改代码的最佳方式?
里德已经解释了为什么.NET异常行为不同于OCaml的例外。 在一般情况下,.NET例外是只适用于特殊情况下,设计用于这一目的。 OCaml有更轻巧的模型,所以他们使用,也能实现一定的控制流模式。
举一个具体的例子,OCaml中可以使用异常实现循环的断裂。 例如,假设你有一个功能test
,测试一个数是否是一个数字,我们想要的。 以下迭代数从1到100,并返回第一匹配数:
// Simple exception used to return the result
exception Returned of int
try
// Iterate over numbers and throw if we find matching number
for n in 0 .. 100 do
printfn "Testing: %d" n
if test n then raise (Returned n)
-1 // Return -1 if not found
with Returned r -> r // Return the result here
要实现这一点没有例外,你有两个选择。 你可以写一个具有相同特性的递归函数-如果你打电话find 0
(和它被编译到基本相同的IL代码使用return n
内for
在C#中环):
let rec find n =
printfn "Testing: %d" n
if n > 100 then -1 // Return -1 if not found
elif test n then n // Return the first found result
else find (n + 1) // Continue iterating
使用递归函数的编码可能有点过长的,但你也可以使用由F#库提供的标准功能。 这通常是重写代码,将使用OCaml的异常控制流的最佳方式。 在这种情况下,你可以这样写:
// Find the first value matching the 'test' predicate
let res = seq { 0 .. 100 } |> Seq.tryFind test
// This returns an option type which is 'None' if the value
// was not found and 'Some(x)' if the value was found.
// You can use pattern matching to return '-1' in the default case:
match res with
| None -> -1
| Some n -> n
如果你不熟悉的选项类型,再看看一些介绍材料。 F#维基有一个很好的教程和MSDN文档有有用的例子太多。
使用来自适当的函数Seq
模块往往使得代码短了很多,所以它是preferrable。 它可能会稍微比直接使用递归效率较低,但在大多数情况下,你不需要担心。
编辑:我很好奇的实际性能。 在使用版本Seq.tryFind
是更有效的,如果输入被懒惰地生成的序列seq { 1 .. 100 }
而不是一个列表[ 1 .. 100 ]
因为列表分配的成本的)。 有了这些变化,并test
函数,返回25元,时间需要我的机器上运行代码100000次:
exceptions 2.400sec
recursion 0.013sec
Seq.tryFind 0.240sec
这是非常琐碎的样本,所以我想使用该解决方案Seq
一般不会跑的比使用递归编写的等效代码慢10倍。 经济放缓可能是由于其他数据结构的分配(对象代表序列,关闭,...),也由于额外的间接(代码需要大量的虚方法调用,而不只是简单的数字运算和跳跃)。 然而,例外是更昂贵和不使代码以任何方式更为简单和易读...
在CLR异常是非常丰富的,并提供了很多细节。 波多黎各马里亚尼上公布的(旧,但仍适用)后异常的成本在其中详细介绍一些这方面的CLR。
这样,在CLR抛出异常具有比在其他一些环境中,包括OCaml的较高的相对成本。
什么是在这种情况下,适当地修改代码的最佳方式?
如果你希望异常在正常,非特殊情况下得到提升,你可以重新考虑你的算法和API在完全避免了异常的方式。 试图提供一种替代的API,其中可以测试之前使异常被提出,例如的情况。
为什么呢,因为CLR的,抛出异常是比较昂贵的,如果F#比OCaml中?
OCaml的沉重的使用异常作为控制流的优化。 相反,在.NET异常尚未在所有优化。
需要注意的是性能上的差异是巨大的 。 例外的是在OCaml的快大约600×比他们在F#。 甚至C ++是在这方面比OCaml的慢大约6×,根据我的基准。
尽管.NET异常涉嫌提供更多(的OCaml提供堆栈跟踪,你还要什么?)我想不出任何理由,他们应该是因为他们是一样慢。
什么是在这种情况下,适当地修改代码的最佳方式?
在F#中,你应该写“总”的功能。 这意味着你的函数应该返回指示样的结果的联合类型的值,例如正常或异常。
特别是,调用find
应该将呼叫更换tryFind
返回的值option
类型要么是Some
价值或None
,如果关键因素是不存在的收藏。