Sum corresponding elements of two lists, with the extra elements of the longer list added at the end - list

I'm trying to add two lists together and keep the extra elements that are unused and add those into the new list e.g.
addLists [1,2,3] [1,3,5,7,9] = [2,5,8,7,9]
I have this so far:
addLists :: Num a => [a] -> [a] -> [a]
addLists xs ys = zipWith (+) xs ys
but unsure of how to get the extra elements into the new list.
and the next step is changing this to a higher order function that takes the combining function
as an argument:
longZip :: (a -> a -> a) -> [a] -> [a] -> [a]

zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] is implemented as [src]:
zipWith :: (a->b->c) -> [a]->[b]->[c]
zipWith f = go
where
go [] _ = []
go _ [] = []
go (x:xs) (y:ys) = f x y : go xs ys
It thus uses explicit recursion where go will check if the two lists are non-empty and in that case yield f x y, otherwise it stops and returns an empty list [].
You can implement a variant of zipWith which will continue, even if one of the lists is empty. THis will look like:
zipLongest :: (a -> a -> a) -> [a] -> [a] -> [a]
zipLongest f = go
where go [] ys = …
go xs [] = …
go (x:xs) (y:ys) = f x y : go xs ys
where you still need to fill in ….

You can do it with higher order functions as simple as
import Data.List (transpose)
addLists :: Num a => [a] -> [a] -> [a]
addLists xs ys = map sum . transpose $ [xs, ys]
because the length of transpose[xs, ys, ...] is the length of the longest list in its argument list, and sum :: (Foldable t, Num a) => t a -> a is already defined to sum the elements of a list (since lists are Foldable).
transpose is used here as a kind of a zip (but cutting on the longest instead of the shortest list), with [] being a default element for the lists addition ++, like 0 is a default element for the numbers addition +:
cutLongest [xs, ys] $
zipWith (++) (map pure xs ++ repeat []) (map pure ys ++ repeat [])
See also:
Zip with default value instead of dropping values?

You're looking for the semialign package. It gives you an operation like zipping, but that keeps going until both lists run out. It also generalizes to types other than lists, such as rose trees. In your case, you'd use it like this:
import Data.Semialign
import Data.These
addLists :: (Semialign f, Num a) => f a -> f a -> f a
addLists = alignWith (mergeThese (+))
longZip :: Semialign f => (a -> a -> a) -> f a -> f a -> f a
longZip = alignWith . mergeThese
The new type signatures are optional. If you want, you can keep using your old ones that restrict them to lists.

Related

Haskell lists combine function

I want to write a recursive function that gets two lists + a requirement as input and outputs all possible tuples with one element each from the 1st and 2nd list that meet the requirement.
It should look something like this:
combine [1,2,3] [5,6,7] (\a b -> a+b > 7) -> [(1,7),(2,6),(2,7),(3,5),(3,6),(3,7)].
I currently just have:
combine:: [a] -> [b] -> [(a, b)]
combine [] ys = []
combine xs [] = []
combine (x:xs) (y:ys) = (x,y) : combine xs ys
but it doesn't filter for anything.
That makes sense, since your input does not filter for anything. You should add an extra parameter here:
combine:: [a] -> [b] -> (a -> b -> Bool) -> [(a, b)]
combine [] ys _ = []
combine xs [] _ = []
combine (x:xs) (y:ys) p
| … = …
| otherwise = …
here p is thus a function that takes an a and a b and returns a Bool, depending on the outcome you thus fire one of the two guards. I leave filling in the … parts as an exercise.
If you want to produce all possible combinations for x and y for which the condition holds, list comprehension is a better tool. You can then work with:
combine:: [a] -> [b] -> (a -> b -> Bool) -> [(a, b)]
combine xs ys p = [ … | … <- xs, … <- ys, … ]

How to enhance small Haskell Code Snippet

just recently I started to try out haskell.
It's fun trying out different exercises, but sometimes I get the feeling, that my found solutions are far from elegant: The following Code Snipplet will find the longest sub-sequence in a list, which will satisfy a given condition (for example uppercase letters etc.)
Could you help a noob to make everything shorter and more elegant - every advice is highly appreciated.
import Data.Char
longer :: [a] -> [a] -> [a]
longer x y = if length x > length y
then x
else y
longest :: [[a]]->[a]
longest = foldl longer []
nextSequence :: (a->Bool) -> [a] ->([a],[a])
nextSequence f x = span f (dropWhile (not . f) x)
longestSubsequence :: (a -> Bool) -> [a] -> [a]
longestSubsequence _ x | null x = []
longestSubsequence f x =
longest $ (\y -> [fst y , longestSubsequence f $ snd y]) (nextSequence f x)
testSequence :: String
testSequence = longestSubsequence Data.Char.isUpper
"hkerhklehrERJKJKJERKJejkrjekERHkhkerHERKLJHERJKHKJHERdjfkj"
At first, you can define your longest like this:
import Data.Function
import Data.List
longest :: [[a]] -> [a]
longest = maximumBy (compare `on` length)
And to get all subsequences that satisfy a given condition you can write a function like this:
import Data.List
getSatisfyingSubseqs :: (a -> Bool) -> [a] -> [[a]]
getSatisfyingSubseqs f = filter (f . head) . groupBy same
where same x y = f x == f y
Here we group elements where the condition yields the same result and filter only subsequences that satisfy the condition.
In the total:
longestSubsequence :: (a -> Bool) -> [a] -> [a]
longestSubsequence f = longest . getSatisfyingSubseqs f
UPDATE: And if you want to make it shorter, you can just throw out the auxiliary functions and write the whole at a time:
longestSubsequence :: (a -> Bool) -> [a] -> [a]
longestSubsequence f = maximumBy (compare `on` length) . filter (f . head) . groupBy same
where same x y = f x == f y
(Don't forget the imports)
You can run it there: https://repl.it/#Yuri12358/so-longestsequence
The span :: (a -> Bool) -> [a] -> ([a], [a]) function could be very handy here. Also note that f <$> (a,b) = (a,f b). Probably not very efficient due to the length checks but it should do the job.
lss :: (a -> Bool) -> [a] -> [a]
lss f [] = []
lss f ls#(x:xs) = if f x then longer (lss f <$> span f ls)
else lss f xs
where
longer ::([a],[a]) -> [a]
longer (xs,ys) = if length xs >= length ys then xs else ys
Your longer function uses length, which means it doesn't work if either input is infinite. However, it can be improved to work when at most one is infinite:
longer l1 l2 = go l1 l2
where
go [] _ = l2
go _ [] = l1
go (_:xs) (_:ys) = go xs ys
This is also a performance optimization. Before, if you had a 10-element list and a 10-million-element list, it would walk through all 10 million elements of the 10-million-element list before returning it. Here, it will return it as soon as it gets to the 11th element instead.

Adding zeros between elements in list?

I'm trying to change a list in haskell to include 0 between every element. If we have initial list [1..20] then i would like to change it to [1,0,2,0,3..20]
What i thought about doing is actually using map on every function, extracting element then adding it to list and use ++[0] to it but not sure if this is the right approach or not. Still learning haskell so might have errors.
My code:
x = map classify[1..20]
classify :: Int -> Int
addingFunction 0 [Int]
addingFunction :: Int -> [a] -> [a]
addingFunction x xs = [a] ++ x ++ xs
intersperse is made for this. Just import Data.List (intersperse), then intersperse 0 yourList.
You cannot do this with map. One of the fundamental properties of map is that its output will always have exactly as many items as its input, because each output element corresponds to one input, and vice versa.
There is a related tool with the necessary power, though:
concatMap :: (a -> [b]) -> [a] -> [b]
This way, each input item can produce zero or more output items. You can use this to build the function you wanted:
between :: a -> [a] -> [a]
sep `between` xs = drop 1 . concatMap insert $ xs
where insert x = [sep, x]
0 `between` [1..10]
[1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10]
Or a more concise definition of between:
between sep = drop 1 . concatMap ((sep :) . pure)
With simple pattern matching it should be:
addingFunction n [] = []
addingFunction n [x] = [x]
addingFunction n (x:xs) = x: n : (addingFunction n xs)
addingFunction 0 [1..20]
=> [1,0,2,0,3,0,4,0,5,0,6,0,7,0,8,0,9,0,10,0,11,0,12,0,13,0,14,0,15,0,16,0,17,0,18,0,19,0,20]
If you want to use map to solve this, you can do something like this:
Have a function that get a int and return 2 element list with int and zero:
addZero :: List
addZero a = [0, a]
Then you can call map with this function:
x = map addZero [1..20] -- this will return [[0,1], [0, 2] ...]
You will notice that it is a nested list. That is just how map work. We need a way to combine the inner list together into just one list. This case we use foldl
combineList :: [[Int]] -> [Int]
combineList list = foldl' (++) [] list
-- [] ++ [0, 1] ++ [0, 2] ...
So the way foldl work in this case is that it accepts a combine function, initial value, and the list to combine.
Since we don't need the first 0 we can drop it:
dropFirst :: [Int] -> [Int]
dropFirst list = case list of
x:xs -> xs
[] -> []
Final code:
x = dropFirst $ combineList $ map addZero [1..20]
addZero :: Int -> [Int]
addZero a = [0, a]
combineList :: [[Int]] -> [Int]
combineList list = foldl (++) [] list
dropFirst :: [Int] -> [Int]
dropFirst list = case list of
x:xs -> xs
[] -> []
We here can make use of a foldr pattern where for each element in the original list, we prepend it with an 0:
addZeros :: Num a => [a] -> [a]
addZeros [] = []
addZeros (x:xs) = x : foldr (((0 :) .) . (:)) [] xs
If you don't want to use intersperse, you can write your own.
intersperse :: a -> [a] -> [a]
intersperse p as = drop 1 [x | a <- as, x <- [p, a]]
If you like, you can use Applicative operations:
import Control.Applicative
intersperse :: a -> [a] -> [a]
intersperse p as = drop 1 $ as <**> [const p, id]
This is basically the definition used in Data.Sequence.

Adding corresponding values of Tuple in a list in Haskell

I have a list for example: [(1,2),(3,4),(5,6)] and the function should return me the sum of the corresponding Tuples. So, this list should return (9,12). I know how to make the tuples add up individually but I have having troubles doing it them together.
sumSecondTuple list = foldl (\acc (x,y) -> (+) y acc) 0 list
sumFirstTuple list = foldl (\acc (x,y) -> (+) x acc) 0 list
I have this much so far. Is there a way I can incooperate them together?
You can also use foldr1:
foldr1 :: Foldable t => (a -> a -> a) -> t a -> a
In this case,
sumSecondTuple :: Num a => [(a,a)] -> (a,a)
sumSecondTuple = foldr1 f
where
f = (\ (a,b) (c,d) -> (a+c,b+d) )
However, sumSecondTuple [] returns an exception Prelude.foldr1: empty list. So, it's better to use foldr than use foldr1 on this occasion.

Working over a list of lists in Haskell

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))
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