Create a new list with a difference from each value
I'm trying to make a function that does the following: it gets a list, for example [1,4,2,3]
, and returns a list [3,2,1]
.
Because 1 - 4 = 3
(abs value), 4 - 2 = 2
and 2 - 3 = 1
.
I thought this piece of code would do it except for the abs value.
function :: [a] -> [a]
function [] = []
function (x:xs) = [x - head(xs)] ++ function xs
but this is giving me errors and I haven't found any solution.
Respectfully,
EDIT:
Thanks guys, you learned a lot today. Indeed I am a beginner, I have a university course that gives me prologue, haskell, scala, python, aspectj and metaprogramming. Thus, we have for each program 2 lessons with 2 hours, and then some time to do some exercises.
Next Monday I have an exam and zipwith etc ... we have to write our own function. But thanks for the nice tutorial explained, learned so much. This is a working solution:
function :: Num a => [a] -> [a]
function (x:y:xs) = [abs(x - y)] ++ function (y:xs)
function _ = []
source to share
If you are still interested in a 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
The operator (-) :: Num a => a -> a -> a
requires its arguments to be Num
. Therefore, we can fix it with function :: Num a => [a] -> [a]
.
We now have another problem:
> function [1,4,2,3]
[-3,2,-1,*** Exception: Prelude.head: empty list
Therefore, we have to handle some cases:
function :: Num a => [a] -> [a]
function [] = []
function [_] = []
function (x:xs) = x - head xs : function xs
But this is still not what we actually expected:
> function [1,4,2,3]
[-3,2,-1]
So, we have to add a function abs
:
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]
Also, you can do it very simply and with more readable code.
How can you tell the difference between the two lists? zipWith
looks really useful. For example:
> zipWith (-) [1,2,3,4] [1,1,1]
[0,1,2]
So the idea is the zipWith (-)
original list and tail
.
> let x = [1,2,3,4]
> zipWith (-) x (tail x)
[-1,-1,-1]
And you may like yours function
:
function :: Num a => [a] -> [a]
function x = map abs $ zipWith (-) x (tail x)
Done
> function [1,4,2,3]
[3,2,1]
source to share
In general, it is a good idea to tell us what errors you get, rather than expecting us to inject your code and run it ourselves. But here's what I see right away:
-
function :: [a] -> [a]
means itfunction
works for any type of list. But you will say laterx - head(xs)
. Here we havex :: a
andhead(xs) :: a
. You would expect the subtraction operator to accept any two values as long as they are of the same type.But
(-) :: Num a => a -> a -> a
; one type is not enough for these two values, this type must implementNum
typeclass (which also provides addition, multiplication, absolute value, and several other functions).Fix: Replace type signature
function :: Num a => [a] -> [a]
. -
The code should now compile, but when you run it, you will get an error with the title of an empty list.
Let's walk through what happens when we run
function [4]
* :-
The first equation has
function []
on the left, but[4]
doesn't match[]
, so skip it. -
The second equation has
function (x:xs)
the left side.[4]
matches(x:xs)
by giving usx = 4
andxs = []
, so we will continue with this equation and estimate[4 - head([])] ++ function []
. -
This includes a score
[4 - head([])]
that includes a score4 - head([])
that includes a scorehead([])
, which is an error because an empty list has no head.
Fix: Think about what we want here. When we match a pattern to a list, it's not enough to know that it has a head (
x
). We also need to know that it has a second item (y
). Therefore, we replace the equations byfunction (x:y:xs) = [x - y] ++ function (y:xs) function _ = []
_
matches any value at all. The first equation is the same when the list contains two or more items, so the second equation will clean up the list of empty lists and single items.* : I am not saying that it really scores in that order. But this is clean code (and final data), so it doesn't matter what order we evaluate.
-
-
You really don't 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 dodge pattern matching with a higher-order function (avoiding, perhaps, that it's wrong ). So take the advice of sclv: use
zipWith
anddrop 1
(and ignore Daniel Fischer's this one time: you don't want to get used to having to worry about how safe to usehead
, for granted, whendrop 1 []
bringing[]
is useful).Other advantages of using a higher-order function instead of pattern matching and explicit recursion:
- only one equation is required
- easier to identify what the code is doing
- more efficient code
source to share
Dmitri's solution could be improved
function :: Num a => [a] -> [a]
function x = map abs $ zipWith (-) x (tail x)
We really want to get the result (-)
and "feed" it immediately to the function abs
, avoiding the extra step map
. Doing it with a lambda expression is pretty ugly:
function :: Num a => [a] -> [a]
function x = zipWith (\a b -> abs(a - b)) x (tail x)
The magic sauce is called "functional composition" and uses an operator (.)
. (f . g) x
means simple f(g x)
but cool that we can glue our functions together and provide an argument later . Using this we get:
function :: Num a => [a] -> [a]
function x = zipWith ((abs.).(-)) x (tail x)
(Corrected after is7s' note, this is a bit tricky since we have to deal with two arguments here)
source to share