Chr*_*lor 38 recursion ocaml functional-programming
可能重复:
为什么Ocaml/F#中的函数默认不递归?
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);;
Run Code Online (Sandbox Code Playgroud)
而在Haskell(GHCI)我可以写
let succ n = n + 1
let fact n =
if n == 0 then 1 else n * fact (n-1)
Run Code Online (Sandbox Code Playgroud)
为什么OCaml区分let和let rec?这是一个性能问题,还是更微妙的东西?
gas*_*che 37
好吧,两者都可用而不是只有一个可以让程序员更加严格地控制范围.用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)desugars into 的宏.我需要这个绑定来避免输出中的代码重复(我不想计算两次).如果绑定不是递归的,那么这只是卫生的,否则我无法写入并获得正确的结果.let x = foo in x * xfooSQUARE(factorial n)factorial nletlet x = 2 in SQUARE(x)
所以我声称确实提供递归和非递归绑定非常重要.现在,let-binding 的默认行为是一个常规问题.你可以说这let x = ...是递归的,必须let nonrec x = ...用来获得非递归的绑定器.选择一个默认值或另一个默认值是您想要支持哪种编程风格,并且有充分的理由做出任何一种选择.Haskell因这种非递归模式不可用而受到损害,而OCaml在类型级别上具有完全相同的缺陷:type foo = ...是递归的,并且没有非递归选项可用 - 请参阅此博客文章.
¹:当谷歌代码搜索可用时,我用它在Haskell代码中搜索模式let x' = sanitize x in ....这是通常的解决方法,当非递归绑定不可用时,但它不太安全,因为你冒险写,x而不是x'以后错误 - 在某些情况下你想要两者都可用,所以选择一个不同的名称可以是自愿的.一个好的习惯用法是为第一个使用更长的变量名x,例如unsanitized_x.无论如何,只是寻找x'字面上(没有其他变量名称)并x1转化了很多结果.Erlang(以及所有试图使变量阴影变得困难的语言:Coffeescript等)都有更糟糕的问题.
也就是说,默认情况下选择Haskell绑定递归(而不是非递归)肯定是有意义的,因为它与默认的延迟评估一致,这使得构建递归值非常容易 - 而严格默认语言对递归定义有意义的限制更多.