最天真的解决方案是简单地独立进行两个评估:
catMaybesCount :: [Maybe a] -> ([a], Int)
catMaybesCount xs = (catMaybes xs, length $ filter isNothing xs)
我不知道 GHC 是否能够正确优化这一点,但无论如何length . filter p
计数的解决方案Nothings
都有一些特殊性(请参阅此 SO 帖子以获取概述)。
从理论上讲,此解决方案可能需要两次遍历列表,而不是一次
这是解决我提出的这个问题的递归解决方案:
import Data.Maybe
-- | Equivalent to @catMaybes@, but additonally counts @Nothing@ values
catMaybesCount :: [Maybe a] -> ([a], Int)
catMaybesCount xs = catMaybesCountWorker xs [] 0
-- | Worker function for @catMaybesCount@
catMaybesCountWorker :: [Maybe a] -> [a] -> Int -> ([a], Int)
catMaybesCountWorker [] justs cnt = (justs, cnt)
catMaybesCountWorker (Nothing:xs) justs cnt =
catMaybesCountWorker xs justs (cnt + 1)
catMaybesCountWorker ((Just v):xs) justs cnt =
catMaybesCountWorker xs (justs ++ [v]) cnt
由于将其应用于列表应该只评估一次列表,这应该更有效。
但是我担心justs ++ [v]
反成语,因为它(:)
会更有效(见这个讨论)。但是,这会反转结果列表。也许对这个主题有更多了解的人可以看看它?
请注意,此函数不会因无限列表而终止,因为Nothing
计数永远不会完成评估。