【发布时间】:2018-04-24 08:38:33
【问题描述】:
考虑以下两种变体:
myReadListTailRecursive :: IO [String]
myReadListTailRecursive = go []
where
go :: [String] -> IO [String]
go l = do {
inp <- getLine;
if (inp == "") then
return l;
else go (inp:l);
}
myReadListOrdinary :: IO [String]
myReadListOrdinary = do
inp <- getLine
if inp == "" then
return []
else
do
moreInps <- myReadListOrdinary
return (inp:moreInps)
在普通编程语言中,人们会知道尾递归变体是更好的选择。
但是,通过this answer,很明显haskell的递归实现与反复使用递归栈的实现并不相似。
但是因为在这种情况下,所讨论的程序涉及动作和严格的单子,我不确定是否适用相同的推理。其实我觉得在IO的情况下,尾递归的形式确实更好。我不确定如何正确推理。
编辑: David Young 指出这里最外面的电话是(>>=)。即使在这种情况下,其中一种风格是否比另一种更有优势?
【问题讨论】:
-
这实际上不是尾递归。如果你对第一个脱糖,你会得到
go l = getLine >>= (\inp -> if (inp == "") then return l else go (inp:l))。最外面的电话是(>>=)。 -
@DavidYoung 对,我完全忘记了这一点。在这种情况下,一种风格比另一种风格有什么优势吗?
-
我认为第一个至少在道德上是尾递归的。相比之下,
f x = bool (x==0) (f (x-1)) 0不是尾递归,但bool最终会将控制权转移给f (x-1),所以它在恒定空间中运行。我想这与 IO 的>>=相同,只是因为我们需要深入研究 IO 实现,所以它不那么容易引起注意。 (另外,请注意,两个发布的 IO 操作的结果是不同的,因为它们以不同的顺序返回列表)
标签: haskell recursion io tail-recursion