haskell create list from lists with different attributes - list

Yes this is a uni assignment question, so please do not just give me the answer, I need to be able to learn what it is and how to do it, (mostly because there are further questions and I need to develop an understanding of the Haskell language to do them!
THE QUESTION:
join :: Eq a => [(a,b)] -> [(a,c)] -> [(a,b,c)].
join takes two lists of pairs, and returns a single list of triples. A triple
is generated only when there exists a member of both argument lists
that have the same first element. The list elements are not sorted. This
is the same semantics as the relational algebra natural join operation.
For example:
join [(2,"S"),(1,"J")] [(2,True),(3,False)])
Should produce the output [(2,"S",True)]
join [(2,"S"),(1,"J")] [(2,1),(2,2),(3,4)])
Should produce the output [(2,"S",1),(2,"S",2)]
My problem
My main problem is trying to figure out how to create a new list from the 2 lists that are input, which have different attributes.
What I have so far:
join :: Eq a => [(a,b)] -> [(a,c)] -> [(a,b,c)]
join xs [] = xs
join [] ys = ys
join (x:xs) (y:ys) = (x ++ snd(head[x]) ++ snd(head[y]) ++ []) join xs ys
The resulting error:
Type error in explicitly typed binding
*** Term : merge
*** Type : [(a,b)] -> [(a,c)] -> [(a,b)]
*** Does not match : [(a,b)] -> [(a,c)] -> [(a,b,c)]
Other notes:
I have tried several variations of the (x ++ snd(head[x]) ++ snd(head[y]) ++ []) section of code, with the results being mostly the same or simular error message!

Your types do not match
join :: Eq a => [(a,b)] -> [(a,c)] -> [(a,b,c)]
join xs [] = xs
join [] ys = ys
-- ^^^^ three different types involved!
xs and ys has type [(a, b)] and [(a, c)] then, they can not be the resultant type [(a,b,c)].
You must to create your resultant data from xs and ys, something like
join ? ? = ... (a, b, c) ...
Can you figure that?
Tip: what is the body of
makeTuple :: (a, b) -> (a, c) -> (a, b, c)
-- ^ ^
-- : |
-- : +-- only one value exists you can put here
-- :
-- +····· only one value exists you can put here
the only two possible functions are
makeTuple (x1, y) (x2, z) = (x1, y, z)
and
makeTuple (x1, y) (x2, z) = (x2, y, z)
In the same way, your join must to preserve the resultant type.
From signature
join :: Eq a => [(a,b)] -> [(a,c)] -> [(a,b,c)]
you know type a is equatable and you know nothing about b and c types.
A simple way to do this is
join xs ys = [(xa, xb, yc) | (xa, xb) <- xs, (ya, yc) <- ys, xa == ya]
-- ^^^^^^^^
-- from `Eq a` constraint

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.

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

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.

How do you convert a list of numbers into a list of ranges in haskell?

Say you have a list of numbers, [1,2,3,5,6,7,8,9,11,12,15,16,17]
and you want a function that takes that as an input and returns something like
[[1,3],[5,9],[11,12],[15,17]] or alternatively maybe
[(1,3), (5,9), (11,12), (15,17)]
how would this be done? all of the solutions i've found online are very very long and quite convoluted, when this seems like such an easy problem for a functional language like haskell
So we have a list of numbers,
xs = [1,2,3,5,6,7,8,9,11,12,14,16,17] -- 14 sic!
We turn it into a list of segments,
ys = [[x,x+1] | x <- xs]
-- [[1,2], [2,3], [3,4], [5,6], ..., [11,12], [12,13], [14,15], [16,17], [17,18] ]
we join the touching segments,
zs = foldr g [] ys
-- [[1,4], [5,10], [11,13], [14,15], [16,18]]
where
g [a,b] [] = [[a,b]]
g [a,b] r#([c,d]:t) | b==c = [a,d]:t
| otherwise = [a,b]:r
and we subtract 1 from each segment's ending value,
ws = [[a,b-1] | [a,b] <- zs]
-- [[1,3], [5,9], [11,12], [14,14], [16,17]]
All in all we get
ranges :: (Num t, Eq t) => [t] -> [[t]]
ranges = map (\[a,b] -> [a,b-1]) . foldr g [] . map (\x -> [x,x+1])
where
g [a,b] [] = [[a,b]]
g [a,b] r#([c,d]:t) | b==c = [a,d]:t
| otherwise = [a,b]:r
Simple and clear.
edit: or, to be properly lazy,
where
g [a,b] r = [a,x]:y
where
(x,y) = case r of ([c,d]:t) | b==c -> (d,t) -- delay forcing
_ -> (b,r)
update: as dfeuer notes, (a,a) type is better than [a,a]. Wherever [P,Q] appears in this code, replace it with (P,Q). This will improve the code, with zero cost to readability.
I would definitely prefer the alternative representation to the first one you give.
ranges :: (Num a, Eq a) => [a] -> [(a,a)]
ranges [] = []
ranges (a : as) = ranges1 a as
-- | A version of 'ranges' for non-empty lists, where
-- the first element is supplied separately.
ranges1 :: (Num a, Eq a) => a -> [a] -> [(a,a)]
ranges1 a as = (a, b) : bs
where
-- Calculate the right endpoint and the rest of the
-- result lazily, when needed.
(b, bs) = finish a as
-- | This takes the left end of the current interval
-- and the rest of the list and produces the right endpoint of
-- that interval and the rest of the result.
finish :: (Num a, Eq a) => a -> [a] -> (a, [(a, a)])
finish l [] = (l, [])
finish l (x : xs)
| x == l + 1 = finish x xs
| otherwise = (l, ranges1 x xs)
To solve the Rosetta Code problem linked in the comment above, this isn't really quite an optimal representation. I'll try to explain how to match the representation more precisely later.
So one might do it like the idea from #Will Ness on the stateful folding or mine under the same answer. All explanations are to be found there. Besides, if you get curious and want to read more about it then have a look at Haskell Continuation Passing Style page. I am currently trying to gerealize this in such a way that we can have a variant of foldr1 in a stateful manner. A foldS :: Foldable t => (a -> a -> b) -> t a -> b. However this is still not general stateful folding. It's just tailored to this question.
ranges :: (Ord a, Num a) => [a] -> [[a]]
ranges xs = foldr go return xs $ []
where
go :: (Ord a, Num a) => a -> ([a] -> [[a]]) -> ([a] -> [[a]])
go c f = \ps -> let rrs#(r:rs) = f [c]
in case ps of
[] -> [c]:r:rs
[p] -> if p + 1 == c then rrs else [p]:(c:r):rs
*Main> ranges [1,2,3,5,6,7,8,9,11,12,15,16,17]
[[1,3],[5,9],[11,12],[15,17]]
I haven't had time to test any edge cases. All advices are welcome.

How can i separate tuples in Haskell?

How can i merge a list of tuples without repeating any items in those tuples ?
for example :
from the list [("a","b"),("c,"d"),("a","b)], it should return ["a","b","c","d"]
So i get this error message with that code:
No instance for (Eq a0) arising from a use of `nub'
The type variable `a0' is ambiguous
Possible cause: the monomorphism restriction applied to the following:
merge :: [(a0, a0)] -> [a0] (bound at P.hs:9:1)
Probable fix: give these definition(s) an explicit type signature
or use -XNoMonomorphismRestriction
Note: there are several potential instances:
instance Eq a => Eq (GHC.Real.Ratio a) -- Defined in `GHC.Real'
instance Eq () -- Defined in `GHC.Classes'
instance (Eq a, Eq b) => Eq (a, b) -- Defined in `GHC.Classes'
...plus 22 others
In the first argument of `(.)', namely `nub'
In the expression: nub . mergeTuples
In an equation for `merge':
merge
= nub . mergeTuples
where
mergeTuples = foldr (\ (a, b) r -> a : b : r) []
Failed, modules loaded: none.
Let's separate this out, first, merge the tuples
mergeTuples :: [(a, a)] -> [a]
mergeTuples = concatMap (\(a, b) -> [a, b]) -- Thanks Chuck
-- mergeTuples = foldr (\(a, b) r -> a : b : r) []
and then we can use nub to make it unique
merge :: Eq a => [(a, a)] -> [a]
merge = nub . mergeTuples
If you want this to all be together
merge = nub . mergeTuples
where mergeTuples = concatMap (\(a, b) -> [a, b])
Or if you want to smash it really together (don't do this)
merge [] = []
merge ((a, b) : r) = a : b : filter (\x -> x /= a && x /= b) (merge r)