在Haskell维基的递归在单子存在被声称是一个例子尾递归 :
f 0 acc = return (reverse acc)
f n acc = do
v <- getLine
f (n-1) (v : acc)
虽然势在必行符号使我们相信它是尾递归,它不是那么明显,在所有(至少对我来说)。 如果我们去糖do
我们得到
f 0 acc = return (reverse acc)
f n acc = getLine >>= \v -> f (n-1) (v : acc)
和重写第二行导致
f n acc = (>>=) getLine (\v -> f (n-1) (v : acc))
所以我们看到, f
的第二个参数内发生>>=
,而不是在尾递归的位置。 我们就需要检查IO
的>>=
得到答案。 显然具有递归调用如在最后一行do
块不是充分条件的函数是尾递归。
比方说,一个单子就是尾递归当且仅当在这个单子每一个递归函数定义为
f = do
...
f ...
或等价
f ... = (...) >>= \x -> f ...
是尾递归。 我的问题是:
- 什么单子是尾递归?
- 有没有我们可以用它来区分立即尾递归单子一些一般性的规则?
更新:让我做一个具体的反例:在[]
单子不是根据上述定义尾递归。 如果是这样,那么
f 0 acc = acc
f n acc = do
r <- acc
f (n - 1) (map (r +) acc)
必须是尾递归。 然而,脱糖第二行导致
f n acc = acc >>= \r -> f (n - 1) (map (r +) acc)
= (flip concatMap) acc (\r -> f (n - 1) (map (r +) acc))
很显然,这并不是尾递归,恕我直言无法进行。 其原因是递归调用不是计算结束。 这是多次执行和结果相结合,使最终结果。