使用作家單子#尾遞歸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