何时需要显式递归?

时间:2014-11-16 21:14:26

标签: haskell recursion idioms

在Haskell中,将尽可能多的代码写入高阶函数(如折叠,贴图和展开)是不恰当的。那么哪些代码不能用这些高阶函数编写?何时需要显式递归?

2 个答案:

答案 0 :(得分:16)

假设我们有一种没有递归的语言或类似的东西。这意味着没有循环结构。这也意味着我们也有(非递归)类型,这样我们就不能形成Y-combinator并逃脱。在这种语言中,我们真的很弱,与我们的许多工具分开。

但我们可以就这种语言提出一个非常好的问题。也就是说,为了使它变得像没有这种限制的语言一样强大,我们必须给它最小的东西是什么?

事实证明有两个答案。

  1. 我们可以引入递归绑定器,例如let rec命令或类似Haskell的let,它总是let rec。换句话说,这个结构允许我们定义let x = e in b,以便x中的e是免费的,然后将其计算为等式x = e上的固定点。

  2. 我们可以引入功能fix :: (a -> a) -> a,以便fix f一步减少到f (fix f)

  3. 从上面的演示中可以清楚地看出,fix可以使用递归绑定器来实现。什么不太清楚的是,递归绑定器可以使用修复从非递归绑定器实现,但我们在这里:

    let x = fix $ \this -> e
    

    this指的是整个表达式,最终绑定为x,这正是我们想要的。


    那么为什么我会不顾一切地说出上述所有内容呢?

    基本上,我想说,只要你愿意考虑{map,就不可能说递归必然是通过HOF组合器实现的fix。{{}} 1}}在该列表上。我还想争辩说,该集合中的组合器实现的任何递归都可以明确地完成"使用递归绑定器。他们同样强大。

    当你自己考虑像foldr / unfoldr这样的HOF组合时,有趣的部分会出现。这些在技术上有点而不是fix /递归绑定器。优点是,如果您仅仅选择一组foldr / unfoldr - 类似原则的编程语言,那么您可以获得一个非常丰富的子图灵完整语言,该语言可以总计或保证终止。

答案 1 :(得分:1)

我认为很多人发现递归数据定义比Mu / Fix / Nu类型更容易阅读。这不是绝对必要的,但在那里非常有用。

类似地,您将使用递归为这样的数据类型编写可折叠/展开实例,但是一旦提供了这些实例,就不需要显式递归了。