I'm a bit of a beginner to Haskell so I'm struggling a little with the strict type stuff, just wondering if someone can help me with a function I'm trying to build. Basically, it takes a list of lists, for example:
[[1,2,3], [7,6,8], [0,3,4]]
and adds them together into one list translating the later lists by the number of positions along it is. So working on the example list it would actually be doing something like:
foldl (zipWith +) [] [[1,2,3],[0,7,6,8],[0,0,0,3,4]]
Here's my current function (which gets type errors):
addLists :: [[Integer]] -> [Integer]
addLists [[]] = []
addLists [[x]] = [x]
addLists [x:xs] = zipWith (+) [x] ([0]++ (addLists xs))
In Haskell, lists are a homogenous data structure. It stores several elements of the same type. That means that we can have a list of integers or a list of characters but we can't have a list that has a few integers and then a few characters.
As you all know, lists in Haskell can be infinite. This capability is a natural consequence of Haskell's lazy evaluation. An infinite list in Haskell is no problem since Haskell will lazily generate only as much of the list as is needed, when it is needed.
Note that ([0]++)
is the same as (0:)
, which will make it look tidier and save us a nanosecond or two.
(I'm joking with the nanosecond thing - no human can tell when something's a nanosecond faster, but it is nicer this way anyway.)
Let's first think about making the lists you need. We want
postponeLists [[1,2,3], [7,6,8], [10,20,30,40]]
= [[1,2,3], [0,7,6,8], [0,0,10,20,30,40]]
= [1,2,3] : ones that should have zero in front of them
That's enough information for a definition:
postponeLists [] = []
postponeLists (l:ls) = l : map (0:) (postponeLists ls)
Now you said
foldl (zipWith +) [] [[1,2,3],[0,7,6,8],[0,0,0,3,4]]
but you mean
foldl (zipWith (+)) [] [[1,2,3],[0,7,6,8],[0,0,0,3,4]]
but unfortunately, that gives you []
because zipWith
stops as soon as any of the lists run out of elements.
We need some way of zipping them that doesn't stop.
Solution 1: find the longest one, make them all that maxlength
using take maxlength.(++ repeat 0)
Solution 2: write another zipWith function that doesn't stop.
I prefer solution 2. Let's look at the definition of zipWith
zipWith :: (a->b->c) -> [a]->[b]->[c]
zipWith f (a:as) (b:bs) = f a b : zipWith f as bs
zipWith _ _ _ = [] -- here's the problem - it stops as soon as any list is empty
OK, let's not stop then:
zipWithMore :: (a -> a -> a) -> [a] -> [a] -> [a]
zipWithMore f (a:as) (b:bs) = f a b : zipWithMore f as bs
zipWithMore f [] bs = bs -- if there's more in bs, use that
zipWithMore f as [] = as -- if there's more in as, use that
Now you can replace zipWith (+)
with zipWithMore (+)
. I'll leave the punchline to you.
I think this does what you want
import Data.List (transpose)
addLists :: Num a => [[a]] -> [a]
addLists xs = map sum . transpose $ zipWith (\n x -> replicate n 0 ++ x) [0..] xs
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With