![]() |
1
23
一如既往,延续产生了一个优雅的追尾解决方案:
有两种测试。首先,这个演示调用F(4)可以根据需要缓存F(4)、F(3)、F(2)、F(1)。
然后,注释掉
下一步,我想,把记忆分解和阶乘分离开来:
编辑
|
![]() |
2
15
记忆尾部递归函数的困境当然是,当尾部递归函数
调用本身时,不允许对递归调用的结果执行任何操作,包括将其放入缓存。狡猾的;那我们该怎么办? 这里的关键是,由于递归函数不允许对递归调用的结果执行任何操作,因此递归调用的所有参数的结果都是相同的!因此,如果递归调用跟踪是这样的
实用注释:上面的阶乘示例说明了一种通用技术。它是非常无用的,因为它是非常充分的阶乘函数缓存的顶级
注意,在这个例子中,当我们从非尾部递归阶乘到尾部递归阶乘时,我们利用了乘法是结合的和交换的这一事实-尾部递归阶乘执行一组不同于非尾部递归阶乘的乘法。 事实上,存在一种从非尾部递归到尾部递归算法的通用技术,它产生一个等价于tee的算法。这种技术被称为“连续传递变换”。沿着这条路线,你可以使用一个非尾部递归记忆阶乘,通过一个机械变换得到一个尾部递归记忆阶乘。关于这个方法的解释,请看布莱恩的答案。 |
![]() |
3
8
我不确定是否有更简单的方法,但一种方法是创建一个记忆y组合器:
然后,您可以使用此组合符代替“let rec”,第一个参数表示要递归调用的函数:
编辑
正如米提亚指出的,
不幸的是,插入到每个递归调用中的机器有点繁重,因此需要深度递归的未记忆输入的性能可能会有点慢。但是,与其他一些解决方案相比,它的优点是对递归函数的自然表达式的更改非常小:
编辑
以前,我们的回忆录过程是这样的:
现在,我们需要考虑一个事实
但是,如果我们愿意更改第一个参数的返回类型(从
同样,如果我们有一个合适的计算表达式来构建CPS函数,我们可以这样定义递归函数:
这与Brian所做的完全相同,但我发现这里的语法更容易理解。为了使这项工作顺利进行,我们只需要以下两个定义:
|
![]() |
4
3
kvb的解决方案返回相同的结果,就像这个函数一样直接记忆。
测试源代码。
|
![]() |
5
0
|
![]() |
Luiz Miranda · 尾递归pow-Erlang 7 年前 |
![]() |
Srinivas · Scala中的尾部递归 7 年前 |
![]() |
madtyn · Python:有可能使这个尾部递归阶乘更快吗? 7 年前 |
![]() |
clay · Scala中的尾部递归findNextAndTail 7 年前 |
![]() |
Sanitiy · 如何在调用另一个函数后强制函数退出? 7 年前 |
![]() |
Lorinc Nyitrai · Lua-将协程递归重写为尾部调用递归 7 年前 |
![]() |
Alex · 尾部递归调用(C primer加上书本示例) 9 年前 |
![]() |
Thomas Ahle · 递归联合查找是否可以优化? 9 年前 |
![]() |
beta Rob · 这个函数真的是尾部递归的吗? 11 年前 |