Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Invert Pair

Tags:

haskell

Wondering if I could get some help writing this function. I am trying to create a function that inverts each "pair" in the list.

module Invert where
invert :: [(a,b)] -> [(b,a)]
invert [(a,b)] = [(b,a)]

When I enter invert [(3,1) (4,1) (5,1)]... it is supposed to give me [(1,3) (1,4) (1,5)... But it gives me...


*Invert> [(3,1) (4,1) (5,1)]

<interactive>:2:2:
    The function `(3, 1)' is applied to two arguments,
    but its type `(t0, t1)' has none
    In the expression: (3, 1) (4, 1) (5, 1)
    In the expression: [(3, 1) (4, 1) (5, 1)]
    In an equation for `it': it = [(3, 1) (4, 1) (5, 1)]
like image 267
Justin Tyler Avatar asked Feb 11 '13 17:02

Justin Tyler


3 Answers

Since lists are recursive data structures, you have to process a list recursively in order to swap all its elements, or use some higher order function that does the processing for you. If you define

invert [(a,b)] = [(b,a)]

it will only convert single-element lists, all other inputs will fail with an error!

Try to think about the input invert gets: It's either an empty list, or a non-empty lists. In the case of a non-empty list, you can swap the first element and convert the rest recursively.

(If you don't want to invert invert yourself, just use

invert = map swap

where swap is from Data.Tuple.)

like image 146
Petr Avatar answered Nov 06 '22 08:11

Petr


So you want to map a function over the list that's of type (a, b) -> (b, a). The function (,) has type b -> a -> (b,a). So if we flip it, we get a -> b -> (b, a). Now if we uncurry that, we get (a, b) -> (b, a):

 invert = map (uncurry $ flip (,))

E.g.

 > map (uncurry $ flip (,)) [(1, "a"), (2, "b")]
 [("a",1),("b",2)]

As an aside, your patten matching doesn't match what you want. The definition

invert [(a,b)] = [(b,a)]

says "match a list with a single tuple in it". If you have a list with multiple tuples, the match will fail. Also, as Josh Lee pointed out, you need commas between tuples in your list.

like image 35
asm Avatar answered Nov 06 '22 08:11

asm


Best way to solve this: split it into smaller problems, and either find library functions that solve those, or write your own. I always tell beginners that this is a superior exercise than trying to write a function like invert in just one part, because you should be learning the following three things:

  1. How to split a problem into small, reusable pieces.
  2. The standard library functions offered by the language.
  3. How to use recursion to write small, reusable functions like the ones in the standard library.

In this case, we can split the problem into:

  1. Inverting an individual tuple.
  2. Applying a function to all elements of a list, and collecting the list of results.

The second one is just the common map function on lists, which comes with the standard library. You could try writing your own version of it; this sort of thing is always a good exercise for a beginner:

map :: (a -> b) -> [a] -> [b]
map f []     = ...
map f (x:xs) = ...

The first, as Petr points out, is the swap function from Data.Tuple. But we can write our own easily:

swap :: (a, b) -> (b, a)
swap (a, b) = (b, a)

And now, of course:

invert :: [(a, b)] -> [(b, a)]
invert = map swap
like image 33
Luis Casillas Avatar answered Nov 06 '22 08:11

Luis Casillas