Reputation: 57
I have to make a function called differences, where I calculate the difference of every pair with zipWith
and put it in a list.
For example differences [1..5] == [1, 1, 1, 1]
.
So [2-1, 3-2, 4-3, 5-4] == [1, 1, 1, 1]
.
I thought of making list of tuples, like this:
[1..5] = [(1,2), (2,3), (3,4), (4,5)]
Then use list comprehesion like this:
[zipWith (-) a b | a <- y, b <- x]
where x is the first element of the tuple and y is the second.
The functions type is differences :: Num a => [a] -> [a]
.
Upvotes: 2
Views: 941
Reputation: 34378
As 4castle suggests, using drop 1
instead of tail
in Robin Zigmond's second, init
-less solution means we can omit the []
case, as drop 1 [] = []
(unlike tail []
, which leads to a runtime error):
differences xs = zipWith (-) (drop 1 xs) xs
To contrast with this solution with no explicit pattern deconstruction, I will mention a spelling using none of init
, tail
and drop
:
differences xs@(_:ys) = zipWith (-) ys xs
Upvotes: 1
Reputation: 531075
Building off Robin Zigmond's answer, the Applicative
instance for functions works well here:
(f <*> g) xs == f xs (g xs)
so
differences = zipWith subtract <*> tail
(where subtract = flip (-)
.)
Upvotes: 3
Reputation: 18249
You're nearly there - but zipWith
itself returns a list, so you don't want to put that inside a list comprehension unless you want the result to be a list of lists (which you don't, here).
zipWith (-)
is absolutely the right idea here - it takes 2 lists and gives a new list given by taking the difference between corresponding elements of the given lists. Your output list, in your case, is intended to be 1 element shorter than the one input list, and you want to use zipWith (-)
on 2 lists which consist of:
Haskell already gives us convenient functions for these, namely tail and init.
So the function you're looking for is:
differences xs = zipWith (-) (tail xs) (init xs)
Note that this isn't ideal, because both init
and tail
will crash your program with an ugly runtime error if xs
is empty. It makes sense to output an empty list if you present an empty list to this function (although you could argue it doesn't, since you will get an empty list from a singleton list), so you can avoid a runtime crash by defining the function via pattern matching to explicitly cater for the empty list:
differences [] = []
differences xs = zipWith (-) (tail xs) (init xs)
While personally I think this is fine, and very explicit, you don't actually need to use both init
and tail
- zipWith
works just fine if presented with lists of unequal length, when it will simply trim the larger one down to size. So differences xs = zipWith (-) (tail xs) xs
is a viable, and slightly terser, alternative.
Upvotes: 3