Reputation: 12538
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
Reputation: 32465
(You can write 1:2:3:4:5:[]
more simply as [1,2,3,4,5]
or even [1..5]
.)
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]
.
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 x
s 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.
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
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
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
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