如何在递归调用链中向后传播信息?
例如:
f :: [Int] -> [Int]
f (x:xs)
| x `mod` 17 = ...
| otherwise = (x + 1) : f xs
f [] = []
我想在...处停止评估,并且还希望将该信息传播回调用方(事实已停止)。我尝试使用Maybe之类的返回类型,但是后来我不得不对递归调用进行模式匹配,从而丢失了尾部调用优化,因为我必须在调用返回后对其进行评估(注意:可以轻松地将上述代码转换为TR表单,但为了便于理解,我将其保留下来。
您能否提出一个仍然可以从TCO中受益的更好的解决方案?
最佳答案
您始终可以使用额外的参数,并返回具有累积结果的元组。这样,您仍然可以从TCO中受益,并获得所需的信息。
一个例子:
f :: [Int] -> Bool -> [Int] -> (Bool, [Int])
f (x:xs) l accum
| x `mod` 17 == 0 = (False, accum)
| otherwise = f xs l ((x+1) : accum)
f [] l accum = (True, accum)
或者以更优雅的方式:
data CheckedValue a = Valid a | Invalid a
deriving Show
f :: [Int] -> [Int] -> CheckedValue [Int]
f (x:xs) accum
| x `mod` 17 == 0 = Invalid accum
| otherwise = f xs ((x+1) : accum)
f [] accum = Valid accum
注意:这些功能也会使列表反向,但是请不要注意这一点。