将其编码为do
递归表示法非常简单。
foo :: ([a] -> Bool) -> [a] -> [[a]]
foo p xs = bar ([],xs)
where
bar (acc,[]) = return acc
bar (acc,xs) = do
(x,ys) <- picks xs -- shrink the domain (ys)
if ( p (x:acc) ) -- test soon
then bar (x:acc,ys) -- loop
else mzero -- fail early
picks [] = []
picks (x : xs) = (x, xs) : [(y, x : ys) | (y, ys) <- picks xs]
picks
来自这个答案 https://stackoverflow.com/questions/12869097/splitting-list-into-a-list-of-possible-tuples/12872133#12872133.
Testing:
> foo (const True) [1..3]
[[3,2,1],[2,3,1],[3,1,2],[1,3,2],[2,1,3],[1,2,3]]
> foo (\(x:xs) -> not(null xs) || x > 1) [1..3]
[[3,1,2],[1,3,2],[2,1,3],[1,2,3]]
最后一个也立即开始产生输出[1..20]
, [1..300]
etc.
我相信这可以用更高层次的东西来清楚地表达。