Reputation: 8118
I'm trying to make a function that does the following: it gets a list for example [1,4,2,3]
and it gives a list back [3,2,1]
.
Because 1 - 4 = 3
(abs value), 4 - 2 = 2
and 2 - 3 = 1
.
I thought this piece of code would do that except for the abs value.
function :: [a] -> [a]
function [] = []
function (x:xs) = [x - head(xs)] ++ function xs
but it's giving me errors and I don't find any solution.
Kind regards,
EDIT:
Thank you guys, learned so much today. Indeed i'm a beginner, i'm having a course on university that gives me prolog,haskell, scala, python,aspectj and metaprogramming. So we have per program lang 2 lessons off 2 hours and afterwards some time to make some exercices.
Next monday i have exam and zipwith, etc... we must write our own function. But thanks for the good explained tutorial, learned so much. This is the working solution:
function :: Num a => [a] -> [a]
function (x:y:xs) = [abs(x - y)] ++ function (y:xs)
function _ = []
Upvotes: 3
Views: 399
Reputation: 4264
I don't know if this suits all your needs, but it worked in ghci:
let func a = [ abs( fst x - snd x ) | x <- zip a (tail a)]
Doing: func [1,4,2,3]
Returns: [3,2,1]
As expected.
I'm still beggining but I'd like to help as I learn too.
Upvotes: 0
Reputation: 54574
Dmitri's solution can be improved
function :: Num a => [a] -> [a]
function x = map abs $ zipWith (-) x (tail x)
What we really want is to take the result of (-)
and "feed" it immediately in the abs
function, avoiding the additional map
step. Doing this with a lambda expression is quite ugly:
function :: Num a => [a] -> [a]
function x = zipWith (\a b -> abs(a - b)) x (tail x)
The magic sauce is called "function composition", and uses the operator (.)
. (f . g) x
means just f(g x)
, but the cool thing is that we can glue our functions together, and provide the argument(s) later. Using this we get:
function :: Num a => [a] -> [a]
function x = zipWith ((abs.).(-)) x (tail x)
(Corrected after is7s remark, it's a little bit more complicated as we have to deal with two arguments here)
Upvotes: 0
Reputation: 47052
In general, it's a good idea to tell us what errors you're getting, instead of expecting us to type your code in and run it ourselves. But this is what I can see at a glance:
function :: [a] -> [a]
means that function
works for a list of any type. But later you say x - head(xs)
. Here we have x :: a
and head(xs) :: a
. You are expecting the subtraction operator to take any two values, so long as they have the same type.
But (-) :: Num a => a -> a -> a
; it's not enough for those two values to have the same type, that type has to implement the Num
typeclass (which also provides addition, multiplication, absolute value and a few other functions).
To fix: Replace the type signature with function :: Num a => [a] -> [a]
.
The code should now compile, but when you run it you will get an error message about taking the head of an empty list.
Let's step through what happens when we run function [4]
*:
The first equation has function []
on the left hand side, but [4]
doesn't match []
, so skip it.
The second equation has function (x:xs)
on the left hand side. [4]
matches (x:xs)
giving us x = 4
and xs = []
, so we'll carry on with this equation and evaluate [4 - head([])] ++ function []
.
This involves evaluating [4 - head([])]
, which involves evaluating 4 - head([])
, which involves evaluating head([])
, which is an error, because an empty list doesn't have a head.
To fix: Think about what we want here. When we pattern match against the list, it's not enough to know that it has a head (x
). We also need to know that it has a second element (y
). So replace the equations with
function (x:y:xs) = [x - y] ++ function (y:xs)
function _ = []
The _
matches any value at all. The first equation matches when the list contains two or more elements, so the second equation will mop up the empty list and single element list cases.
*: I'm not saying it actually gets evaluated in this order. But this is pure code (and finite data), so it doesn't matter what order we evaluate things in.
You don't really want to do explicit recursion yourself if you can help it, you want to use a higher order function instead; partly because it means you don't have to write the recursion yourself (and risk getting it wrong), and partly because you can often palm off the pattern matching onto the higher order function as well (avoiding possibly getting that wrong as well). So take sclv's advice: use zipWith
and drop 1
(and ignore Daniel Fischer this once: you don't want to get into the habit of needing to care whether it's safe to use head
as a matter of course, when drop 1 []
yielding []
is useful).
Other advantages of using a higher order function instead of pattern matching and explicit recursion:
Upvotes: 5
Reputation: 21972
If you are still interested in recursive solution.
No instance for (Num a)
arising from a use of `-'
In the expression: x - head (xs)
In the first argument of `(++)', namely `[x - head (xs)]'
In the expression: [x - head (xs)] ++ function xs
So (-) :: Num a => a -> a -> a
operator requires, that it's arguments should be Num
. So we can fix it with function :: Num a => [a] -> [a]
.
Now we have another problem:
> function [1,4,2,3]
[-3,2,-1,*** Exception: Prelude.head: empty list
So we should handle some cases:
function :: Num a => [a] -> [a]
function [] = []
function [_] = []
function (x:xs) = x - head xs : function xs
But it's still not what we actually expected:
> function [1,4,2,3]
[-3,2,-1]
So we should add abs
function:
function :: Num a => [a] -> [a]
function [] = []
function [_] = []
function (x:xs) = abs ( x - head xs ) : function xs
Done
> function [1,4,2,3]
[3,2,1]
Beside that you can do that really easy and with more readable code.
How can you find a difference between two lists? zipWith
looks really helpful here. For example:
> zipWith (-) [1,2,3,4] [1,1,1]
[0,1,2]
So idea is to zipWith (-)
original list and it's tail
.
> let x = [1,2,3,4]
> zipWith (-) x (tail x)
[-1,-1,-1]
And your function
could like like that:
function :: Num a => [a] -> [a]
function x = map abs $ zipWith (-) x (tail x)
Done
> function [1,4,2,3]
[3,2,1]
Upvotes: 5