AnchovyLegend
AnchovyLegend

Reputation: 12538

Haskell List Comprehension creating function

I am new to Haskell and am trying to learn the basics. I am having a hard time understanding how to manipulate the contents of a list.

Assume I have the following list and I would like to create a function to subtract 1 from every element in the list, where I can simply pass x to the function, how would this be done?

Prelude>let x = 1:2:3:4:5:[]

Something like:

Prelude>subtractOne(x)

Upvotes: 3

Views: 9289

Answers (4)

AndrewC
AndrewC

Reputation: 32465

(You can write 1:2:3:4:5:[] more simply as [1,2,3,4,5] or even [1..5].)

Comprehensions

You'd like to use list comprehensions, so here it is:

subtractOne xs = [ x-1 | x <- xs ]

Here I'm using xs to stand for the list I'm subtracting one from.

The first thing to notice is x <- xs which you can read as "x is taken from xs". This means we're going to take each of the numbers in xs in turn, and each time we'll call the number x.

x-1 is the value we're calculating and returning for each x.

For more examples, here's one that adds one to each element [x+1|x<-xs] or squares each element [x*x|x<-xs].

More than one list

Let's take list comprehension a little further, to write a function that finds the squares then the cubes of the numbers we give it, so

> squaresAndCubes [1..5]
[1,4,9,16,25,1,8,27,64,125]

We need

squaresAndCubes xs = [x^p | p <- [2,3], x <- xs]

This means we take the powers p to be 2 then 3, and for each power we take all the xs from xs, and calculate x to the power p (x^p).

What happens if we do that the other way around?

squaresAndCubesTogether xs = = [x^p | x <- xs, p <- [2,3]]

We get

> squaresAndCubesTogether [1..5]
[1,1,4,8,9,27,16,64,25,125]

Which takes each x and then gives you the two powers of it straight after each other.

Conclusion - the order of the <- bits tells you the order of the output.

Filtering

What if we wanted to only allow some answers?

Which numbers between 2 and 100 can be written as x^y?

> [x^y|x<-[2..100],y<-[2..100],x^y<100]
[4,8,16,32,64,9,27,81,16,64,25,36,49,64,81]

Here we allowed all x and all y as long as x^y<100.


Since we're doing exactly the same to each element, I'd write this in practice using map:

takeOne xs = map (subtract 1) xs

or shorter as

takeOne = map (subtract 1)

(I have to call it subtract 1 because - 1 would be parsed as negative 1.)

Upvotes: 13

Code-Apprentice
Code-Apprentice

Reputation: 83577

You can do this quite easily with the map function, but I suspect you want to roll something yourself as a learning exercise. One way to do this in Haskell is to use recursion. This means you need to break the function into two cases. The first case is usually the base case for the simplest kind of input. For a list, this is an empty list []. The result of subtracting one from all the elements of the empty list is clearly an empty list. In Haskell:

subtractOne [] = []

Now we need to consider the slightly more complex recursive case. For any list other than an empty list, we can look at the head and tail of the input list. We will subtract one from the head and then apply subtractOne to the rest of the list. Then we need to concatenate the results together to form a new list. In code, this looks like this:

subtractOne (x:xs) = (x - 1) : subtractOne xs

As I mentioned earlier, you can also do this with map. In fact, it is only one line and the preferred Haskellism. On the other hand, I think it is a very good idea to write your own functions which use explicit recursion in order to understand how it works. Eventually, you may even want to write your own map function for further practice.

Upvotes: 2

nameless
nameless

Reputation: 1979

You can do this with the map function:

subtractOne = map (subtract 1)

The alternative solution with List Comprehensions is a little more verbose:

subtractOne xs = [ x - 1 | x <- xs ]

You may also want to add type annotations for clarity.

Upvotes: 8

Pubby
Pubby

Reputation: 53097

map (subtract 1) x will work.

subtractOne = map (subtract 1) 

The map function allows you to apply a function to each element of a list.

Upvotes: 1

Related Questions