Given two lists, [a, b]
and [c, d]
, I'd like to get the following result:
[(a,c), (a,d), (b,c), (b,d)]
How can I do this in Haskell? Is there a built-in function for this, or should I implement one myself?
[ (x,y) | x<-[a,b], y<-[c,d] ]
This doesn't really require any further explanation, does it?
Applicative style all the way!
λ> :m + Control.Applicative
λ> (,) <$> ['a','b'] <*> ['c','d']
[('a','c'),('a','d'),('b','c'),('b','d')]
(I've eschewed any String
syntactic sugar above, in order to stay close to your example.)
For information, (,)
is special syntax for a function that takes two arguments and makes a pair out of them:
λ> :t (,)
(,) :: a -> b -> (a, b)
Edit: As noted by leftaroundabout in his comment, you can also use liftA2
:
λ> :m + Control.Applicative
λ> let combine = liftA2 (,)
λ> combine "ab" "cd"
[('a','c'),('a','d'),('b','c'),('b','d')]
How can you do this in an imperative pseudocode?
for each element x in [a,b]:
for each element y in [c,d]:
produce (x,y)
In Haskell, this is written as
outerProduct xs ys =
do
x <- xs -- for each x drawn from xs:
y <- ys -- for each y drawn from ys:
return (x,y) -- produce the (x,y) pair
(following comments by leftaroundabout) this is of course extremely close to how liftM2
monadic combinator is defined, so in fact
outerProduct = liftM2 (,)
which is the same as liftA2 (,)
, and its various re-writes in terms of list comprehensions, concatMap
function, >>=
, <$>
and <*>
operators.
Conceptually though this is the stuff of the Applicative
– which would be better named as Pairing
, – because this pairing up of the elements of two "containers" ⁄ "carriers" ⁄ whatever is exactly what Applicative Functor is about. It just so happens that Haskell's do
notation works for monads, and not (yet) for applicatives.
In some sense compile-time nested loops are Applicative ⁄ Pairing functors; Monads add the ability to create nested loops on the fly, depending on the values produced by an "outer" enumeration.
The most inuitive would be using list comprehension, other aproaches include using applicative functors:
(,) <$> [1,2,3] <*> [4,5,6]
So what does this do?
Remember that (,) :: a -> b -> (a, b)
Takes two arguments and returns a tuple.
<$>
is acutally fmap, (<$>) :: Functor f => (a -> b) -> f a -> f b
It takes a function and lift it. In this case it takes (,)
and lift it to work on list. So let x = (,) <$> [1,2]
would generate x :: [b -> (Integer, b)]
which is the the list of functions that takes b
and returns tuple with one fixed argument (Integer,b). Finally we apply it using <*>
to generate all the combinations.
use List Comprehension:
s = [a,b]
s' = [c,d]
all_combinations = [(x,y) | x <- s, y <- s']
来源:https://stackoverflow.com/questions/32093912/all-combinations-of-elements-of-two-lists-in-haskell