简体   繁体   English

何时需要显式递归?

[英]When is explicit recursion necessary?

In Haskell, it is idiomatic to write as much of your code in higher-order functions like folds, maps, and unfolds as possible. 在Haskell中,尽可能在高阶函数(如折叠,贴图和展开)中编写尽可能多的代码是惯用的。 So what kinds of code can't be written with those higher-order functions? 那么哪些代码不能用那些高阶函数编写? When is explicit recursion necessary? 何时需要显式递归?

Let's assume we have a language without recursion or anything like it. 让我们假设我们有一种没有递归的语言或类似的东西。 This means no looping structures either. 这意味着没有循环结构。 It also means we have (non-recursive) types as well so that we can't form a Y-combinator and escape. 这也意味着我们也有(非递归)类型,因此我们无法形成Y-combinator并逃脱。 In this language, we are truly weak, separated from so many of our tools. 在这种语言中,我们真的很弱,与我们的许多工具分开。

But we can ask a very good question about this language. 但我们可以就这种语言提出一个非常好的问题。 Namely, what is the smallest thing we must give it in order for it to become just as powerful as a language with no such restrictions? 也就是说,为了使它变得像没有这种限制的语言一样强大,我们必须给它最小的东西是什么?

It turns out there are two answers. 事实证明有两个答案。

  1. We can introduce recursive binders, like a let rec command or something like Haskell's let which is always let rec . 我们可以引入递归绑定器,比如let rec命令或类似Haskell的let ,它总是let rec In other words, a structure which lets us define let x = e in b such that if x is free in e then it is computed as a fixed point on the equation x = e . 换句话说,一种允许我们let x = e in b定义let x = e in b的结构,使得如果xe是自由的,那么它被计算为等式x = e上的固定点。

  2. We can introduce the function fix :: (a -> a) -> a such that fix f reduces in one step to f (fix f) . 我们可以引入函数fix :: (a -> a) -> a这样fix f在一步减少到f (fix f)

It should be clear from the above presentation that fix can be implemented using recursive binders. 从上面的演示中可以清楚地看出,可以使用递归绑定器来实现fix What's a little less clear is that recursive binders can be implemented from non-recursive ones using fix, but here we are: 有点不太清楚的是递归绑定器可以使用修复从非递归绑定器实现,但是我们在这里:

let x = fix $ \this -> e

The value this refers to the whole expression which ends up bound as x which is just what we want. this是指整个表达式,最终结合为x这是我们想要什么。


So why did I go out of my way to say all of the above? 那么为什么我会不遗余力地说出以上所有内容呢?

Essentially, I'd like to argue that it's not possible to say that recursion is necessarily implemented via HOF combinators like map so long as you're willing to consider fix on that list. 从本质上讲,我想说,只要你愿意考虑fix该列表,就不可能说递归必然是通过像map这样的HOF组合器来实现的。 I'd also like to argue that any recursion implemented by combinators in that set can be done "explicitly" using recursive binders. 我还想争辩说,该集合中的组合器实现的任何递归都可以使用递归绑定器“显式”完成。 They're equally powerful. 他们同样强大。

The interesting part comes in when you consider HOF combinators like foldr / unfoldr by themselves. 当您考虑HOF组合器(如foldr / unfoldr时,有趣的部分会出现。 These are technically somewhat weaker than fix /recursive binders. 这些在技术上比fix /递归绑定器稍弱 The advantage is that if you build a programming language off only a select set of foldr / unfoldr -like principles then you can get a very rich, sub-Turing complete language which can be total or guaranteed to terminate. 优点是,如果您只使用一组选择的foldr / unfoldr原则构建编程语言,那么您可以获得一个非常丰富的子图灵完整语言,该语言可以完全或保证终止。

I think a lot of people find recursive data definitions easier to read than Mu/Fix/Nu types. 我想很多人发现递归数据定义比Mu / Fix / Nu类型更容易阅读。 It's not strictly necessary, but very useful there. 这不是绝对必要的,但在那里非常有用。

Similarly, you'll write the Foldable/Unfoldabe instances for such a data type by using recursion, but once those are provided, explicit recursion is not required going forward. 类似地,您将通过使用递归为这样的数据类型编写可折叠/ Unfoldabe实例,但是一旦提供了这些实例,就不需要进行显式递归。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM