私はライターの結果を結合反復ごとに再帰関数(収束、ニュートン法)を持っている場合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)
を、私はこれが尾を再帰的にしてはいけないと考えています(再帰呼び出しから判断されたように見えるかもしれませんが)。
再帰が終了するまで(そして結合されるまで)、すべてのログをキューに保持する必要があるためです。
したがって、Writerのバインドが末尾呼び出しではなく再帰を行うのが正しいかどうかという質問が1つあります。 2番目の質問は、いずれかのモナドにするかどうかを切り替えている:どちらか一方のバインドの論理があるので
//...
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
//...
:
let bind ma fm =
match ma with
| Ok a -> fm a
| Err e -> Err e
はこの今も末尾再帰を行います:バインドと
type Result<'e, 'a> =
| Ok of 'a
| Err of 'e
短絡?または、adjustment >>=
がスタック位置を保持できますか?
EDIT:
だから、明確な答えの光の中で、私は明確にし、私の質問に答えることができます - 今末尾呼び出し位置は「推移的」であるか否かのようなものになります。 (1)nextStep
への再帰呼び出しは、nextStep
の末尾の呼び出しです。 (2)nextStep
への(初期)コールはbind
(私のEither
/Result
モナド)の末尾コールです。 (3)bind
は、外側(再帰的)solve
関数のテールコールです。
テールコール解析と最適化はこのネストを実行しますか?はい。
あなたの '> = '演算子を' bind'の単純な呼び出しとして定義したとすると、 'adjustment >> = nextStep'という行は' bind adjust nextStep'とまったく同じです。スタックフレームの場合は**、**の場合は**のみ機能します。ここでは、 'bind'コールは末尾にあり、余分なスタックフレームはありません。詳細は以下の私の答えを見てください。 – rmunn
はい、それはあなたが演算子のために言う通りです: 'let(>> =)ma fm = Result.bind ma fm'。 – RomnieEE