|
|
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
|