最初にいくつかの背景。私は現在、モナディックパーサーコンビネーターについていくつかのことを学んでいます。この論文(p。16-17)から「chainl1」関数を転送しようとしたときに、次の解決策を思いつきました。
let chainl1 p op = parser {
let! x = p
let rec chainl1' (acc : 'a) : Parser<'a> =
let p' = parser {
let! f = op
let! y = p
return! chainl1' (f acc y)
}
p' <|> succeed acc
return! chainl1' x
}
大きな入力で関数をテストしたところ、StackOverflowExceptionが発生しました。今、私は疑問に思っていますが、末尾再帰を使用するような方法で、何らかの計算式を使用する再帰関数を書き直すことは可能ですか?
計算式を展開すると、一般的にどのようにできるのかわかりません。
let chainl1 p op =
let b = parser
b.Bind(p, (fun x ->
let rec chainl1' (acc : 'a) : Parser<'a> =
let p' =
let b = parser
b.Bind(op, (fun f ->
b.Bind(p, (fun y ->
b.ReturnFrom(chainl1' (f acc y))))))
p' <|> succeed acc
b.ReturnFrom(chainl1' x)))