一些背景優先。我目前正在學習一些關於monadic解析器組合器的東西。當我試圖從this paper(第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)))
這擺脫了通過用戶代碼遞歸,但在我的實現這裏http://lorgonblog.spaces.live.com/blog/cns!701679AD17B6D310!1772.entry它仍然StackOverflows通過解析器實現本身。我現在會被激發去研究'延續解析器'... – Brian 2010-06-29 18:31:55
FParsec http://www.quanttec.com/fparsec/處理這個問題嗎? – Brian 2010-06-29 18:33:00
布賴恩,我也用你的博客系列作爲學習資源。它幫助了很多。同時我將Mau的答案('seq')與我的解析器進行了比較。我猜想monad中的Delay方法是導入。但我真的不知道。 FParsec使用'while'...但我想使用功能性解決方案:D – PetPaulsen 2010-06-29 18:50:15