Haskell lists combine function - list

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, … ]

Related

Haskell Function which combines Lists

I want to write a recursive function that gets two lists + a conditional as input, and outputs all possible tuples with one element each from the 1st and 2nd lists that satisfy the condition.
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 got this atm:
Combine:: [a] -> [b] -> [(a, b)]
Combine [] ys = []
Combine xs [] = []
Combine (x:xs) (y:ys) = (x,y) : Combine xs ys
However, it does not yet create all possible combinations and does not filter by condition. I really don't know how to figure this out.
Thanks in advance
You don't need a recursive function, but you need a higher order function.
combinationsFilter :: (a -> b -> Bool) -> [a] -> [b] -> [(a,b)]
combinationsFilter f as bs = filter (uncurry f) [(a, b) | a <- as, b <- bs]
[(a, b) | a <- as, b <- bs] generates all combinations of a and b.
filter... just filters the list by condition.
uncurry needed if you want pass a function with type (a -> b -> c), but not ((a, b) -> c). It converts one to the other.

How do I split a list on certain conditions in Haskell?

As a programming exercise I'm trying to build a function in Haskell where given a list it splits the list whenever an element is repeated. [1,2,3,3,4,5] would split into [[1,2,3],[3,4,5]] for example. My first idea was to split the list into a list of lists with single elements, where [1,2,3,3,4,5] would become [[1],[2],[3],[3],[4],[5]] and then merge lists only when the elements being compared are not equal, but implementing this has been a headache for me as I'm very new to Haskell and recursion has always given me trouble. I think something is wrong with the function I'm using to combine the lists, it will only ever return a list where all the elements that were broken apart are combined, where [1,2,3,3,4,5] becomes [[1],[2],[3],[3],[4],[5]] but my split_help function will transform this into [[1,2,3,3,4,5]] instead of [[1,2,3],[3,4,5]]
I've pasted my incomplete code below, it doesn't work right now but it should give the general idea of what I'm trying to accomplish. Any feedback on general Haskell code etiquette would also be welcome.
split_breaker breaks the list into a list of list and split_help is what I'm trying to use to combine unequal elements.
split_help x y
| x /= y = x ++ y
| otherwise = []
split_breaker :: Eq a => [a] -> [[a]]
split_breaker [] = []
split_breaker [x] = [[x]]
split_breaker (x:xs) = [x]:split_breaker xs
split_at_duplicate :: Eq a => [a] -> [[a]]
split_at_duplicate [x] = [[x]]
split_at_duplicate (x:xs) = foldl1 (split_help) (split_breaker [xs])
Do you want to work it something like this?
splitAtDup [1,2,3,3,3,4,4,5,5,5,5,6]
[[1,2,3],[3],[3,4],[4,5],[5],[5],[5,6]]
Am I right?
Then do it simple:
splitAtDup :: Eq a => [a] -> [[a]]
splitAtDup (x : y : xs) | x == y = [x] : splitAtDup (y : xs)
splitAtDup (x : xs) =
case splitAtDup xs of
x' : xs' -> (x : x') : xs'
_ -> [[x]]
splitAtDup [] = []
Here's a maximally lazy approach:
splitWhen :: (a -> a -> Bool) -> [a] -> [[a]]
splitWhen f = foldr go [[]] where
go x acc = (x:xs):xss where
xs:xss = case acc of
(z:_):_ | f x z -> []:acc
_ -> acc
splitAtDup :: Eq a => [a] -> [[a]]
splitAtDup = splitWhen (==)
To verify the laziness, try this:
take 2 $ take 4 <$> splitAtDup (1:2:3:3:4:5:6:undefined)
It can be fully evaluated to normal form as [[1,2,3],[3,4,5,6]].

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

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.

Breaking up a list into sublists with recursion

I'm trying to write a function with the type declaration [(Int, Bool)] -> [[Int]]. I want the function to only add Ints to the same nested sublist if the Boolean is True. However if the Boolean is False, I want the Int associated with the next True bool to be added to a new sublist. For example: An input of
[(1,True),(2,True),(3,False),(4,True),(5,False),(6,False),(7,True)]
should return
[[1,2],[4],[7]].
My code so far:
test:: [(Int, Bool)] -> [[Int]]
test xs = case xs of
[]->[]
x:xs
| snd x == True -> [(fst x)] : test xs
| snd x == False -> test xs
I'm currently having issues on adding concurrent Ints to the same list if their bools are both True.
You can break this problem into two sub-problems.
For any given list, take the head of this list and match it against the rest of list. There are two possibilities during this matching: i) You are successful i.e. you match, and if so, you collect the matched value and continue looking for more values, or ii) You fail, i.e. you don't match, and if so, you stop immediately and return the so far matched result with rest of, not-inspected, list.
collectF :: (Eq a) => (a -> Bool) -> [a] -> ([a], [a])
collectF f [] = ([], [])
collectF f (x : xs)
| f x = let (ys, zs) = collectF f xs in (x : ys, zs)
| otherwise = ([], x : xs)
Now that you have the collectF function, you can use it recursively on input list. In each call, you would get a successful list with rest of, not-inspected, list. Apply collectF again on rest of list until it is exhausted.
groupBy :: (Eq a) => (a -> a -> Bool) -> [a] -> [[a]]
groupBy _ [] = []
groupBy f (x : xs) =
let (ys, zs) = collectF (f x) xs in
(x : ys) : groupBy f zs
*Main> groupBy (\x y -> snd x == snd y) [(1,True),(2,True),(3,False),(4,True),(5,False),(6,False),(7,True)]
[[(1,True),(2,True)],[(3,False)],[(4,True)],[(5,False),(6,False)],[(7,True)]]
I am leaving it to you to remove the True and False values from List. Also, have a look at List library of Haskell [1]. Hope, I am clear enough, but let me know if you have any other question.
[1] http://hackage.haskell.org/package/base-4.12.0.0/docs/src/Data.OldList.html#groupBy
Repeatedly, drop the Falses, grab the Trues. With view patterns:
{-# LANGUAGE ViewPatterns #-}
test :: [(a, Bool)] -> [[a]]
test (span snd . dropWhile (not . snd) -> (a,b))
| null a = []
| otherwise = map fst a : test b
Works with infinite lists as well, inasmuch as possible.
Here's how I'd write this:
import Data.List.NonEmpty (NonEmpty(..), (<|))
import qualified Data.List.NonEmpty as NE
test :: [(Int, Bool)] -> [[Int]]
test = NE.filter (not . null) . foldr go ([]:|[])
where
go :: (Int, Bool) -> NonEmpty [Int] -> NonEmpty [Int]
go (n, True) ~(h:|t) = (n:h):|t
go (n, False) l = []<|l
Or with Will Ness's suggestion:
import Data.List.NonEmpty (NonEmpty(..))
test :: [(Int, Bool)] -> [[Int]]
test = removeHeadIfEmpty . foldr prependOrStartNewList ([]:|[])
where
prependOrStartNewList :: (Int, Bool) -> NonEmpty [Int] -> NonEmpty [Int]
prependOrStartNewList (n, True) ~(h:|t) = (n:h):|t
prependOrStartNewList (n, False) l = []:|removeHeadIfEmpty l
removeHeadIfEmpty :: NonEmpty [Int] -> [[Int]]
removeHeadIfEmpty (h:|t) = if null h then t else h:t

Selecting string elements in a list using integer elements from another list

I'm going to use an example to explain my question because I'm not sure the best way to put it into words.
Lets say I have two lists a and b:
a = ["car", "bike", "train"] and b = [1, 3]
And I want to create a new list c by selecting the items in a whose positions correspond to the integers in b, so list c = ["car", "train"]
How would I do this in Haskell? I think I have to use list comprehension but am unsure how. Cheers.
The straightfoward way to do this is using the (!!) :: [a] -> Int -> a operator that, for a given list and zero-based index, gives the i-th element.
So you could do this with the following list comprehension:
filterIndex :: [a] -> [Int] -> [a]
filterIndex a b = [a!!(i-1) | i <- b]
However this is not efficient since (!!) runs in O(k) with k the index. Usually if you work with lists you try to prevent looking up the i-th index.
In case it is guaranteed that b is sorted, you can make it more efficient with:
-- Only if b is guaranteed to be sorted
filterIndex = filterIndex' 1
where filterIndex' _ _ [] = []
filterIndex' i a:as2 js#(j:js2) | i == j = a : tl js2
| otherwise = tl js
where tl = filterIndex' (i+1) as2
Or even more efficient:
-- Only if b is guaranteed to be sorted
filterIndex = filterIndex' 1
where filterIndex' i l (j:js) | (a:as) <- drop (j-i) l = a : filterIndex' (j+1) as (js)
filterIndex' _ _ [] = []
I am going to assume you're using b = [0, 2] instead (lists are 0 indexed in Haskell).
You can use a fold to build the new list:
selectIndices :: [a] -> [Int] -> [a]
selectIndices as is = foldr (\i bs -> as !! i : bs) [] is
This starts with an empty list and adds new elements by selecting them from the list of as using an index i from the list of indices is.
More advanced: if you prefer a point-free style, the same function can be written:
selectIndices :: [a] -> [Int] -> [a]
selectIndices as = foldr ((:) . (as !!)) []
Another approach which could be more efficient if the indices are sorted would be to go through the list one element at a time while keeping track of the current index:
selectIndices :: [a] -> [Int] -> [a]
selectIndices as is = go as 0 (sort is)
where
go :: [a] -> Int -> [Int] -> [a]
go [] _ _ = []
go _ _ [] = []
go (a:as) n (i:is)
| n == i = a : go as (n + 1) is
| otherwise = go as (n + 1) (i:is)
A simple approach is tagging the values in a with the indices and then filtering according to the indices:
filterIndex :: [Int] -> [a] -> [a]
filterIndex b = fmap snd . filter (\(i, _) -> i `elem` b) . zip [1..]
-- non-point-free version:
-- filterIndex b a = fmap snd (filter (\(i, _) -> i `elem` b) (zip [1..] a))
(If you want zero-based rather than one-based indexing, just change the infinite list to [0..]. You can even parameterise it with something like [initial..].)
If you need to make this more efficient, you might consider, among other things, a filtering algorithm that exploits ordering in b (cf. the answers by Boomerang and Willem Van Onsem), and building a dictionary from the zip [1..] a list of pairs.