最も素朴な解決策は、単純に両方の評価を個別に行うことです。
catMaybesCount :: [Maybe a] -> ([a], Int)
catMaybesCount xs = (catMaybes xs, length $ filter isNothing xs)
GHC がこれを適切に最適化できるかどうかはわかりませんが、length . filter p
カウントのソリューションにNothings
はいくつかの特徴があります (概要については、この SO の投稿を参照してください)。
理論的には、このソリューションでは、リストに対して 1 回ではなく 2 回のパスが必要になる可能性があります。
これは、私が思いついたこの問題を解決する再帰的なソリューションです。
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
カウントの評価が終了しないため、この関数は無限リストでは終了しないことに注意してください。