f# - 为什么 OCaml/F# 中的函数默认不是递归的?

标签 f# recursion ocaml

为什么 F# 和 OCaml(可能还有其他语言)中的函数默认情况下不是递归的?

换句话说,为什么语言设计者认为在如下声明中显式地输入 rec 是个好主意:

let rec foo ... = ...

并且默认情况下不赋予函数递归能力?为什么需要显式的 rec 构造?

最佳答案

原始 ML 的法国和英国后裔做出了不同的选择,他们的选择几十年来一直被继承到现代变体。所以这只是遗留问题,但它确实影响了这些语言中的习语。

在法语 CAML 语言系列(包括 OCaml)中,函数默认不是递归的。这种选择使得在这些语言中使用 let 可以轻松地取代函数(和变量)定义,因为您可以在新定义的主体中引用先前的定义。 F# 从 OCaml 继承了此语法。

例如,在 OCaml 中计算序列的香农熵时取代函数 p:

let shannon fold p =
  let p x = p x *. log(p x) /. log 2.0 in
  let p t x = t +. p x in
  -. fold p 0.0

请注意高阶 shannon 函数的参数 p 如何被主体第一行中的另一个 p 取代,然后正文第二行中的另一个 p

相反,ML 语言家族的英国 SML 分支采取了另一种选择,并且 SML 的 fun 绑定(bind)函数默认是递归的。当大多数函数定义不需要访问其函数名称的先前绑定(bind)时,这会导致代码更简单。但是,被取代的函数使用不同的名称(f1f2 等),这会污染作用域,并可能意外调用函数的错误“版本” 。现在,隐式递归 fun 绑定(bind)函数和非递归 val 绑定(bind)函数之间存在差异。

Haskell 可以通过将定义限制为纯定义来推断定义之间的依赖关系。这使得玩具 sample 看起来更简单,但在其他方面却付出了巨大的代价。

请注意,Ganesh 和 Eddie 给出的答案是转移注意力的。他们解释了为什么不能将函数组放置在巨大的 let rec ... and ... 中,因为它会影响类型变量的泛化时间。这与 SML 中默认的 rec 无关,但 OCaml 中则不然。

关于f# - 为什么 OCaml/F# 中的函数默认不是递归的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/900585/

相关文章:

.net - 如何从函数返回格式化的字符串?

c - 递归打印链表时出现段错误

css - 带有复选框的递归 mustache 制作下拉菜单

algorithm - 递归函数返回错误值

compiler-errors - 顶级Ocaml无穷解释循环

casting - F# 转换一个 int

generics - 相互定义的参数中的 F# 通用单位

sorting - OCaml - 如何对对进行排序?

python - Python 闭包中有什么?对于习惯 OCaml 的人来说有哪些注意事项?

f# - 在线回复中引用 FSharp.Data