使用作家单子#尾递归f控制单子绑定
let inline bind ma fm =
let (Writer (a, log1)) = ma
let mb = fm a
let (Writer (b, log2)) = mb
let sum = (^w : (static member add : ^w * ^w -> ^w) (log1, log2))
Writer (b, sum)
如果我有一个递归函数(收敛,牛顿法)随每次迭代结合作家结果,我想,这一定不会是尾递归(尽管它可能看起来像它只是从递归调用判断):
let solve params =
let rec solve guess iteration =
let (adjustment : Writer<Error,float>) = getAdjustment params guess
let nextStep adjustment =
if (abs adjustment) <= (abs params.tolerance) then
Writer.rtn guess
elif iteration > params.maxIter then
Writer (0.0, Error.OverMaxIter)
else
solve (guess + adjustment) (iteration + 1)
adjustment >>= nextStep
sovle params.guess 1
,因为所有日志都必须保存在队列中,直到递归结束(然后加入)。
所以,一个问题是Writer上的绑定是否使得递归不是尾调用是正确的。第二个问题是,是否切换到要么单子:
type Result<'e, 'a> =
| Ok of 'a
| Err of 'e
与绑定:
let bind ma fm =
match ma with
| Ok a -> fm a
| Err e -> Err e
将使这现在是尾递归:
//...
let (adjustment : Result<Error,float>) = getAdjustment params guess
let nextStep adjustment =
if (abs adjustment) <= (abs params.tolerance) then
Result.rtn guess
elif iteration > params.maxIter then
Result.fail Error.OverMaxIter
else
solve (guess + adjustment) (iteration + 1)
adjustment >>= nextStep
//...
由于无论哪种的绑定的逻辑是短路?或者这个adjustment >>=
能够保持在堆叠位置?
编辑:
因此,在明确的答案的光,我可以澄清和回答我的问题 - 现在相当于像尾调用位置是否是“传递”。 (1)nextStep
的递归调用是nextStep
的尾部调用。 (2)对nextStep
的(初始)呼叫是bind
(我的Either
/Result
单子)的尾呼。 (3)bind
是外部(递归)solve
函数中的尾部调用。
尾部调用分析和优化是否进行嵌套?是。
假设您已经定义了'>> ='运营商作为以'bind'一个简单的通话,线路调整'= >>是nextStep'完全等同于'绑定调整nextStep':它会坚持到堆栈帧如果和**仅**如果功能做更多的工作。因为这里'bind'调用处于尾部位置,所以不会有额外的栈帧。查看我的答案以获得更多信息。 – rmunn
是的,它是如你所说的操作:'让(>> =)MA FM = Result.bind马fm'。 – RomnieEE