OCaml使用let定义新函数,使用let rec定义递归函数。为什么它需要这两种方法--我们就不能用let来做任何事情吗?
例如,要在OCaml中(实际上,在OCaml解释器中)定义一个非递归的后续函数和递归阶乘,我可以编写
let succ n = n + 1;;
let rec fact n =
if n = 0 then 1 else n * fact (n-1);;而在Haskell (GHCI),我可以写
let succ n = n + 1
let fact n =
if n == 0 then 1 else n * fact (n-1)为什么OCaml要区分let和let rec?这是性能问题,还是更微妙的问题?
发布于 2012-02-17 12:12:11
好的,两者都可用,而不是只有一个,这使程序员对范围的控制更加严格。在let x = e1 in e2中,绑定只存在于e2的环境中,而与let rec x = e1 in e2的绑定则同时存在于e1和e2的环境中。
(编辑:我想强调的是,这不是一个性能问题,这一点都没有区别。)
这里有两种情况,其中非递归绑定非常有用:
let f x = (let x = sanitize x in ...),其中sanitize是一个确保输入具有某些理想属性的函数(例如。它采用可能非归一化向量的范数,等等)。这在某些情况下是非常有用的。SQUARE(foo)定义一个宏foo,该宏将把糖块定义为let x = foo in x * x。我需要这种绑定来避免输出中的代码重复(我不希望SQUARE(factorial n)计算factorial n两次)。只有在let绑定不是递归的情况下,这才是卫生的,否则我无法编写let x = 2 in SQUARE(x)并获得正确的结果。因此,我认为,同时具有递归绑定和非递归绑定是非常重要的。现在,let绑定的默认行为是一个惯例问题。您可以说let x = ...是递归的,必须使用let nonrec x = ...来获得非递归绑定。选择一种默认或另一种是你希望选择哪种编程风格的问题,有很好的理由做出这两种选择。Haskell的缺点是这种非递归模式不可用,而OCaml在类型级别上也有相同的缺陷:type foo = ...是递归的,没有非递归的选项--参见这篇博客文章。
1:当Google搜索可用时,我用它在Haskell代码中搜索模式let x' = sanitize x in ...。当非递归绑定不可用时,这是通常的解决办法,但不太安全,因为以后可能会错误地编写x而不是x' --在某些情况下,您希望两者都可用,因此选择不同的名称可以是自愿的。一个好的成语是为第一个x使用较长的变量名,例如unsanitized_x。不管怎么说,只要从字面上寻找x' (没有其他变量名)和x1就会产生很多结果。Erlang (以及所有试图使可变阴影变得困难的语言: Coffeescript等)有更严重的问题。
尽管如此,在默认情况下选择Haskell绑定递归(而不是非递归)当然是有意义的,因为它与默认情况下的惰性计算是一致的,这使得构建递归值变得非常容易,而严格默认语言对递归定义有更多的限制。
https://stackoverflow.com/questions/9325888
复制相似问题