首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么ocaml同时需要"let“和"let rec"?

为什么ocaml同时需要"let“和"let rec"?
EN

Stack Overflow用户
提问于 2012-02-17 09:38:24
回答 1查看 17.8K关注 0票数 44

可能重复: 为什么Ocaml/F#中的函数在默认情况下不是递归的?

OCaml使用let定义新函数,使用let rec定义递归函数。为什么它需要这两种方法--我们就不能用let来做任何事情吗?

例如,要在OCaml中(实际上,在OCaml解释器中)定义一个非递归的后续函数和递归阶乘,我可以编写

代码语言:javascript
复制
let succ n = n + 1;;

let rec fact n =
    if n = 0 then 1 else n * fact (n-1);;

而在Haskell (GHCI),我可以写

代码语言:javascript
复制
let succ n = n + 1

let fact n =
    if n == 0 then 1 else n * fact (n-1)

为什么OCaml要区分letlet rec?这是性能问题,还是更微妙的问题?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-02-17 12:12:11

好的,两者都可用,而不是只有一个,这使程序员对范围的控制更加严格。在let x = e1 in e2中,绑定只存在于e2的环境中,而与let rec x = e1 in e2的绑定则同时存在于e1e2的环境中。

(编辑:我想强调的是,这不是一个性能问题,这一点都没有区别。)

这里有两种情况,其中非递归绑定非常有用:

  • 用使用旧绑定的精化来隐藏现有定义。类似于: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绑定递归(而不是非递归)当然是有意义的,因为它与默认情况下的惰性计算是一致的,这使得构建递归值变得非常容易,而严格默认语言对递归定义有更多的限制。

票数 39
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9325888

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档