我有以下代码,它旨在获取a
的列表和b
的列表,并返回所有配对[(a,b)],这样
a
和每个b
仅在每个配对中出现一次。 (a, b)
符合条件cond
,即cond :: a -> b -> Bool
。例如,列表[1,2] [x,y,z]的结果应为
[[(1, x), (2, y)]
[(1, x), (2, z)]
[(1, y), (2, x)]
[(1, y), (2, z)]
[(1, z), (2, x)]
[(1, z), (2, y)]]
这是一些(有点抽象的)代码,它使用显式递归来完成工作,但我想用折叠或类似的东西替换它。有什么提示吗?
someFn :: [a] -> [b] -> [ [(a, b)] ]
someFn [] _ = []
someFn (a : as) bs = [ [(a,b)] ++ rest | b <- bs, rest <- someFn as (bs \\ [b]), cond a b]
答案 0 :(得分:5)
从你的解释中我可以理解你想要根据两个列表的产品上的某些条件进行过滤。使用列表推导很容易获取列表的产品,然后过滤函数会将产品减少到只满足给定条件的对
foo :: [a] -> [b] -> (a -> b -> Bool)-> [(a,b)]
foo x y with = filter (uncurry with) [(a,b) | a <- x, b <- y]
[根据编辑更新]
这会产生你想要的列表(希望如此)
bar :: [a] -> [b] -> [[(a,b)]]
bar xs ys = map (zip xs) $ permutations ys
过滤给定条件
biz :: (a -> b -> Bool) -> [[(a,b)]] -> [[(a,b)]]
biz = map . filter . uncurry
答案 1 :(得分:0)
您可以使用foldr
重构代码,如下所示:
delete :: Int -> [a] -> [a]
delete index xs = let (ys, _:zs) = splitAt index xs in ys ++ zs
ifoldr :: (Int -> a -> b -> b) -> b -> [a] -> b
ifoldr f acc xs = foldr (\(a, b) c -> f a b c) acc $ zip [0..] xs
someFn :: (a -> b -> Bool) -> [a] -> [b] -> [[(a,b)]]
someFn _ [] _ = [[]]
someFn cond (a:as) bs = ifoldr (\index b acc -> if cond a b
then concat [map ((a,b):) . someFn cond as $ delete index bs, acc]
else acc) [] bs
请注意,边缘大小写为someFn _ [] _ = [[]]
,这与函数someFn :: (a -> b -> Bool) -> [a] -> [b] -> [[(a,b)]]
的类型定义一致。
您可以按如下方式使用someFn
:
someFn (\a b -> True) [1,2] "xyz"
-- [[(1,'x'),(2,'y')],
-- [(1,'x'),(2,'z')],
-- [(1,'y'),(2,'x')],
-- [(1,'y'),(2,'z')],
-- [(1,'z'),(2,'x')],
-- [(1,'z'),(2,'y')]]
someFn (\a b -> case (a,b) of (1,'x') -> False
(2,'y') -> False
otherwise -> True) [1,2] "xyz"
-- [[(1,'y'),(2,'x')],
-- [(1,'y'),(2,'z')],
-- [(1,'z'),(2,'x')]]
希望有所帮助。