Reputation: 415
I have the following code, which is designed to take a list of a
's , and list of b
's, and return all pairings [(a, b)], such that
a
and each b
only appears once in each pairing. (a, b)
meets some condition cond
, i.e. cond :: a -> b -> Bool
.For example, the result for the lists [1, 2] [x,y,z] should be
[[(1, x), (2, y)]
[(1, x), (2, z)]
[(1, y), (2, x)]
[(1, y), (2, z)]
[(1, z), (2, x)]
[(1, z), (2, y)]]
Here is some (somewhat abstracted) code that that does the job with explicit recursion, but I'd like to replace it with a fold or something similar. Any tips?
someFn :: [a] -> [b] -> [ [(a, b)] ]
someFn [] _ = []
someFn (a : as) bs = [ [(a,b)] ++ rest | b <- bs, rest <- someFn as (bs \\ [b]), cond a b]
Upvotes: 5
Views: 800
Reputation: 74234
You can use a foldr
to refactor your code as follows:
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
Note that the edge case is someFn _ [] _ = [[]]
which is in accordance with the type definition of the function someFn :: (a -> b -> Bool) -> [a] -> [b] -> [[(a,b)]]
.
You may use someFn
as follows:
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')]]
Hope that helps.
Upvotes: 0
Reputation: 11218
From what I can comprehend from your explanation is you want to filter based on some condition on the product of two list. It is easy to take product of lists using list comprehension and then the filter function will reduce the product to only pairs satisfying the given condition
foo :: [a] -> [b] -> (a -> b -> Bool)-> [(a,b)]
foo x y with = filter (uncurry with) [(a,b) | a <- x, b <- y]
[Update according to edit]
This produces the list like you want (hopefully)
bar :: [a] -> [b] -> [[(a,b)]]
bar xs ys = map (zip xs) $ permutations ys
To filter on the given condition
biz :: (a -> b -> Bool) -> [[(a,b)]] -> [[(a,b)]]
biz = map . filter . uncurry
Upvotes: 5