1

I am trying to get all combinations of a list. The result for [1,2,3] should be [(1,2),(1,3),(2,3)]. My implementation below, however, gives [(1,2),(2,3)].

parings [d] = []
parings (y:ys) = (y, head ys): parings ys
duplode
  • 33,731
  • 7
  • 79
  • 150
  • Also see https://stackoverflow.com/questions/28191103/iterate-over-all-pair-combinations-without-repetition-in-haskell – chi Mar 16 '18 at 19:55

3 Answers3

2

The list comprehension mentioned in 9000's answer can be trivially factored into a map call.

pairwise :: [a] -> [(a, a)]
pairwise [] = []
pairwise (x:xs) = map (\y -> (x, y)) xs ++ pairwise xs

Every list comprehension can be factored into some combination of map, filter, and concatMap, possibly with some let bindings interspersed. Doing so is a good exercise for learning how to manipulate functions.

Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
1

Here is one implementation using tails from Data.List:

import Data.List

pairings :: [a] -> [(a, a)]
pairings = concatMap makePairs . tails
    where
    makePairs [] = []
    makePairs (x:xs) = fmap ((,) x) xs

Notes:

  • I don't know whether tails counts as a "special import" for you -- though it is not in the Prelude, it can be found in the base library, which is always available.

  • To see what tails does, try tails [1..3].

  • ((,) x) is just a compact way of writing (\y -> (x, y)). If you find it ugly, you can write the longer version instead, or enable the TupleSections extension and spell it as (x,).

  • makePairs might be written without explicit recursion as...

    makePairs = maybe [] (\(x, xs) -> fmap ((,) x) xs) . uncons
    

    ... with uncons also being found in Data.List.

  • All the implementations in the answers here have a not insignificant problem: they retain consumed list segments in memory. To see what I'm talking about, watch the memory usage as you run head . drop 8000000 $ pairings [1..] in GHCi. I'm not confident about there being a workaround for that -- a simple concat . tails, for instance, appears to run into the same issue, while fmap makePairs . tails doesn't (even head . drop (10^9) . head . drop (10^9) . fmap makePairs . tails $ [1..] won't eat up all your memory).

duplode
  • 33,731
  • 7
  • 79
  • 150
0

I don't know why you're opposed to list comprehensions; with them, the solution is trivial:

pairwise :: [a] -> [(a, a)]
pairwise [] = []
pairwise (x:xs) = [(x, y) | y <- xs] ++ pairwise xs

You can factor out the comprehension into a separate function with explicit tail recursion if you want.

(The whole thing can be made tail-recursive, too, by keeping parameters for both sides of the ++ and having an accumulator parameter.)

9000
  • 39,899
  • 9
  • 66
  • 104
  • awesome possum! And the reason for not using list comprehension is because I am challenging myself to learn algorithms and what goes through them internally. What if one day list comprehension breaks and we need to use normal algorithms haha i dont know im tired – megabowser56 Mar 16 '18 at 04:04