Related
I am learning Haskell.
I am trying to find elements of a list as which sum to elements of a list bs, returning the elements as a tuple:
findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]
findSum2 as bs = [(a, a', b) | a <- as, a' <- as, b <- bs, a + a' == b]
The code works. But in order to learn Haskell, I'm trying to rewrite it as do-notation:
findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]
findSum2 as bs = do
a <- as
a' <- as
b <- bs
if a + a' == b then return (a, a', b)
else return ()
The type-checker then complains at me:
• Couldn't match type ‘()’ with ‘(Int, Int, Int)’
Expected type: [(Int, Int, Int)]
Actual type: [()]
In all fairness, I knew it would. But since I can't skip the else clause in Haskell, what should I put in the return statement in the else clause?
Thanks.
You must return something of the correct type in the else clause. It could be the empty list [], or one of the abstracted values like mzero or empty.
Or you could remove the if expression and use the guard function.
import Control.Monad (guard)
findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]
findSum2 as bs = do
a <- as
a' <- as
b <- bs
guard (a + a' == b)
return (a, a', b)
With this implementation you could now also generalize your function signature to:
findSum2 :: MonadPlus m => m Int -> m Int -> m (Int, Int, Int)
You can not return the unit (()), since that means that the return (a, a', b) and the return () have different types: the first one is [(Int, Int, Int)], whereas the latter is [()].
What you can do is use an empty list in case the guard fails, so:
findSum2 :: [Int] -> [Int] -> [(Int,Int,Int)]
findSum2 as bs = do
a <- as
a' <- as
b <- bs
if a + a' == b then return (a, a', b) else []
I am new to Haskell and I want to extract the maximum element from a given List so that I end up with the maximum element x and the remaining list xs (not containing x). It can be assumed that the elements of the list are unique.
The type of function I want to implement is somewhat like this:
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
Notably, the first argument is a function that turns an element into a comparable form. Also, this function is non-total as it would fail given an empty List.
My current approach fails to keep the elements in the remainder list in place, meaning given [5, 2, 4, 6] it returns (6, [2, 4, 5]) instead of (6, [5, 2, 4]). Furthermore, it feels like there should be a nicer looking solution.
compareElement :: (Ord b) => (a -> b) -> a -> (b, (a, [a])) -> (b, (a, [a]))
compareElement p x (s, (t, ts))
| s' > s = (s', (x, t:ts))
| otherwise = (s, (t, x:ts))
where s' = p x
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
maxElement p (t:ts) = snd . foldr (compareElement p) (p t, (t, [])) $ ts
UPDATE
Thanks to the help of the answer of #Ismor and the comment #chi I've updated my implementation and I feel happy with the result.
maxElement :: (Ord b) => (a -> b) -> [a] -> Maybe (b, a, [a], [a])
maxElement p =
let
f x Nothing = Just (p x, x, [], [x])
f x (Just (s, m, xs, ys))
| s' > s = Just (s', x, ys, x:ys)
| otherwise = Just (s, m, x:xs, x:ys)
where s' = p x
in
foldr f Nothing
The result is either Nothing when the given list is empty or Maybe (_, x, xs, _). I could write another "wrapper" function with the originally intended type and call maxElement under the hood, but I believe this also ok.
This answer is more of a personal advise than a proper answer. As a rule of thumb, whenever you find yourself trying to write a loop with an accumulator (as in this case), try to write it in this form
foldr updateAccumulator initialAccumulator --use foldl' if it is better for your use case`
then, follow the types to complete It as shown below
Step 1
Write undefined where needed. You know the function should look like this
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
maxElement f xs = foldr updateAccumulator initalAccumulator xs
where
updateAccumulator = undefined
initialAccumulator = undefined
Step 2
"Chase the type". Meaning that using the type of maxElement and foldr you can
deduce the types of updateAccumulator and initialAccumulator. Try to reduce polymorphism as much as you can. In this case:
You know foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
You know your Foldable is [] so It'd be easier to substitute
Hence foldr :: (a -> b -> b) -> b -> [a] -> b
Because you want foldr to produce (a, [a]) you know b ~ (a, [a])
etc... keep going until you know what types your functions have. You can use ghc typed holes in this process, which is a very nice feature
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
maxElement f xs = foldr updateAccumulator initalAccumulator xs
where
-- Notice that you need to enable an extension to write type signature in where clause
-- updateAccumulator :: a -> (a, [a]) -> (a, [a])
updateAccumulator newElement (currentMax, currentList) = undefined
-- initialAccumulator :: (a, [a])
initialAccumulator = undefined
Step 3
Now, writing down the function should be easier. Below I leave some incomplete parts for you to fill
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
maxElement f xs = foldr updateAccumulator initalAccumulator xs
where
-- updateAccumulator :: a -> (a, [a]) -> (a, [a])
updateAccumulator newElement (currentMax, currentList) =
if f newElement > f currentMax
then undefined -- How does the accumulator should look when the new element is bigger than the previous maximum?
else undefined
-- initialAccumulator :: (a, [a])
initialAccumulator = undefined -- Tricky!, what does happen if xs is empty?
Hope this clarifies some doubts, and understand I don't give you a complete answer.
I don't know if you were trying to avoid using certain library functions, but Data.List has a maximumBy and deleteBy that do exactly what you want:
import Data.Function (on)
import Data.List (deleteBy, maximumBy)
import Data.Ord (comparing)
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
maxElement f xs = (max, remaining) where
max = maximumBy (comparing f) xs
remaining = deleteBy ((==) `on` f) max xs
Thanks to the help of the answer of #Ismor and the comment #chi I've updated my implementation and I feel happy with the result.
maxElement :: (Ord b) => (a -> b) -> [a] -> Maybe (b, a, [a], [a])
maxElement p =
let
f x Nothing = Just (p x, x, [], [x])
f x (Just (s, m, xs, ys))
| s' > s = Just (s', x, ys, x:ys)
| otherwise = Just (s, m, x:xs, x:ys)
where s' = p x
in
foldr f Nothing
The result is either Nothing when the given list is empty or Maybe (_, x, xs, _). I could write another "wrapper" function with the originally intended type and call maxElement under the hood, but I believe this is also ok.
Construct the list of all the "zippers" over the input list, then take the maximumBy (comparing (\(_,x,_) -> foo x)) of it, where foo is your Ord b => a -> b function, then reverse-append the first half to the second and put it in a tuple together with the middle element.
A zipper over a list xs is a triple (revpx, x, suffx) where xs == reverse revpx ++ [x] ++ suffx:
> :t comparing (\(_,x,_) -> x)
comparing (\(_,x,_) -> x)
:: Ord a => (t, a, t1) -> (t, a, t1) -> Ordering
Constructing the zippers list is an elementary exercise (see the function picks3 there).
About your edited solution, it can be coded as a foldr over the tails so it's a bit clearer what's going on there:
maxElement :: (Ord b) => (a -> b) -> [a] -> Maybe (b, a, [a])
maxElement p [] = Nothing
maxElement p xs = Just $ foldr f undefined (tails xs)
where
f [x] _ = (p x, x, [])
f (x:xs) (b, m, ys)
| b' > b = (b', x, xs) -- switch over
| otherwise = (b, m, x:ys)
where b' = p x
It's also a bit cleaner as it doesn't return the input list's copy for no apparent reason, as your version did since it used it for internal purposes.
Both ways are in fact emulating a paramorphism.
I have to compare a tuple with a list of tuples and returns True if the integers are less than any of the tuples in the list. Like if i have superM ("Tomato",10,5) [("Potato",5,6),("Orange",11,6)] will return True because the integers in the alone Tuple ("Tomate",10,5) are smaller then the tuple("Orange",11,6) in the list, but if i have superM ("Melon",10,6) [("Potato",5,6),("Orange",11,6)] will return False.
I try this
superM :: (String, Int, Int) -> [(String, Int, Int)] -> Bool
superM (s,a,b) ((_,c,d):xs)
| a < c && b < d = True
|otherwise = superM (s,a,b) xs
But doesn't work when it suppose to return False and I don't know why?
Note:The Strings doesn't matter for this problem, i must ignore it.
You did not define a basecase for the empty list. So if no element matches, eventually the list will be exhausted, and then the empty list will not match. You thus can add a rule for the empty list:
superM :: (String, Int, Int) -> [(String, Int, Int)] -> Bool
superM _ [] = False
superM (s,a,b) ((_,c,d):xs)
| a < c && b < d = True
|otherwise = superM (s,a,b) xs
we can make use of a logical or to get rid of the guards:
superM :: (String, Int, Int) -> [(String, Int, Int)] -> Bool
superM _ [] = False
superM (s,a,b) ((_,c,d):xs) = a < c && b < d || superM (s,a,b) xs
but we can also use the any :: Foldable f => (a -> Bool) -> f a -> Bool function to let this work with any Foldable:
superM :: (Foldable f, Ord x, Ord y) => (a, x, y) -> f (b, x, y) -> Bool
superM (_, a, b) = any p
where p (_, c, d) = a < c && b < d
I have a list of 3 tuples items, I would like to index the list based on the first item, I have already written a code that sounds logically sane to me yet am getting a type error, here's what I wrote
addIndex [] indexed = indexed
addIndex ((a1,b1,c1):xs) []
= addIndex xs [(a1,b1,c1,0)]
addIndex ((a1,b1,c1):xs) indexedWIP
= addIndexH ((a1,b1,c1):xs) indexedWIP (last indexedWIP)
addIndexH ((a1,b1,c1):xs) indexedWIP (ax,bx,cx,ix)
= if (a1 /= ax)
then (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix+1))))
else (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix))))
I'm getting the following type error
ERROR file:.\lmaogetrektson.hs:109 - Type error in application
*** Expression : indexedWIP ++ (a1,b1,c1,ix + 1)
*** Term : (a1,b1,c1,ix + 1)
*** Type : (b,c,d,e)
*** Does not match : [a]
Let me examine the types of your addIndex at each row:
addIndex :: [a] -> b -> b
addIndex [] indexed = indexed
-- Combined with the above, leads to:
addIndex :: (Num n) => [(a,b,c)] -> [(a,b,c,n)] -> [(a,b,c,n)]
addIndex ((a1,b1,c1):xs) [] = addIndex xs [(a1,b1,c1,0)]
-- This call demands addIndexH satisfies:
addIndexH :: (Num n) => [(a,b,c)] -> [(a,b,c,n)] -> (a,b,c,n) -> [(a,b,c,n)]
-- It's also costly, as last needs to traverse the list
addIndex ((a1,b1,c1):xs) indexedWIP =
addIndexH ((a1,b1,c1):xs) indexedWIP (last indexedWIP)
-- /= check matches types of a1 and ax, requiring them to be Eq
addIndexH ((a1,b1,c1):xs) indexedWIP (ax,bx,cx,ix) =
if (a1 /= ax) then (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix+1))))
else (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix))))
The distinction of list and tuple is actually the problem you hit here.
Prelude> :t (++)
(++) :: [a] -> [a] -> [a]
Both operands to ++ must be same-type lists. So we need something like:
addIndexH ((a1,b1,c1):xs) indexedWIP (ax,bx,cx,ix) =
if (a1 /= ax) then (addIndex xs (indexedWIP ++ [(a1,b1,c1,(ix+1))]))
else (addIndex xs (indexedWIP ++ [(a1,b1,c1,(ix))]))
The end result should be a function that takes a list of 3-tuples and another list of enumerated 4-tuples, but in a rather circuitous manner. Consider how it expands:
addIndex [(a,b,c), (x,y,z)] []
addIndex [(x,y,z)] [(a,b,c,0)]
addIndexH [(x,y,z)] [(a,b,c,0)] (a,b,c,0)
addIndex [] ([(a,b,c,0)] ++ [(x,y,z,(0+1))])
([(a,b,c,0)] ++ [(x,y,z,(0+1))])
That's a fairly complex procedure, and it grows worse the longer the lists are (we haven't even looked at duplicate a fields yet).
When you do encounter a duplicate a field, you still append it, only keeping the new index value. This means, since we only checked against the last item, that we have two items of matching a and index right next to each other. The function could be rewritten in several ways, in particular without rebuilding lists of every intermediate length and traversing the growing one for each element.
I think you make it more complex than necessary. If I understand it correctly, you take as input a list of 3-tuples (a, b, c), and you want to return a list of 4-tuples (a, b, c, i), where i specifies the thus far number of different a-values we observed.
We thus perform some sort of mapping but with an accumulator. Although we can use higher-order constructs here, let us here aim to use recursion and add an accumulator. We can first define a helper function with signature:
addIndex' :: (Num n, Eq a) => a -> n -> [(a, b, c)] -> [(a, b, c, n)]
where the first parameter is thus the a-value of the previous element (we here assume that we processed already an element). The second parameter is the number of elements we thus far observed, the third elements is the list of elements we still have to process, and the result is the list of 4-tuples.
In case the list is exhausted, then we can return the empty list, regardless of the other variables:
addIndex' _ _ [] = []
in the other case, we should compare the previous key ap with the current key a, and in case the two are equal, we return the tuple with the index i as last element, we then recurse with the same index; otherwise we increment the index (to i1 = i + 1). We each time recurse on the tail of the list:
addIndex' ap i ((a, b, c): xs) | a == ap = (a, b, c, i) : addIndex' a i xs
| otherwise = (a, b, c, i1) : addIndex' a i1 xs
where i1 = i + 1
So we obtain the function:
addIndex' :: (Num n, Eq a) => a -> n -> [(a, b, c)] -> [(a, b, c, n)]
addIndex' _ _ [] = []
addIndex' ap i ((a, b, c): xs) | a == ap = (a, b, c, i) : addIndex' a i xs
| otherwise = (a, b, c, i1) : addIndex' a i1 xs
where i1 = i + 1
But now we still have to process the first element. We know that if the list is empty, we return the empty list:
addIndex [] = []
otherwise we return as first tuple the first one in the given list with index 0, and then make a call to addIndex' with the remaining tuples and the first key as accumulator:
addIndex ((a, b, c): xs) = (a, b, c, 0) : addIndex' a 0 xs
so we obtain as full solution:
addIndex :: (Num n, Eq a) => [(a, b, c)] -> [(a, b, c, n)]
addIndex [] = []
addIndex ((a, b, c): xs) = (a, b, c, 0) : addIndex' a 0 xs
addIndex' :: (Num n, Eq a) => a -> n -> [(a, b, c)] -> [(a, b, c, n)]
addIndex' _ _ [] = []
addIndex' ap i ((a, b, c): xs) | a == ap = (a, b, c, i) : addIndex' a i xs
| otherwise = (a, b, c, i1) : addIndex' a i1 xs
where i1 = i + 1
Then we generate for example:
Prelude> addIndex [('a', 1, 4), ('a', 2, 5), ('b', 1, 3), ('b', 0, 2), ('c', 1, 2)]
[('a',1,4,0),('a',2,5,0),('b',1,3,1),('b',0,2,1),('c',1,2,2)]
But note that we only look at the previous element, and hence for example if the 'a' key occurs after 'c', we will increment the counter again:
Prelude> addIndex [('a', 1, 4), ('a', 2, 5), ('b', 1, 3), ('b', 0, 2), ('c', 1, 2), ('a', 3, 4)]
[('a',1,4,0),('a',2,5,0),('b',1,3,1),('b',0,2,1),('c',1,2,2),('a',3,4,3)]
This function will run in linear time O(n) whereas the functions you composed will run in quadratic time O(n2) since appending is done in linear time (as well as last, etc.).
So basically I have a list of tuples [(a,b)], from which i have to do some filtering. One job is to remove inverted duplicates such that if (a,b) and (b,a) exist in the list, I only take one instance of them. But the list comprehension has not been very helpful. How to go about this in an efficient manner?
Thanks
Perhaps an efficient way to do so (O(n log(n))) would be to track the tuples (and their reverses) already added, using Set:
import qualified Data.Set as Set
removeDups' :: Ord a => [(a, a)] -> Set.Set (a, a) -> [(a, a)]
removeDups' [] _ = []
removeDups' ((a, b):tl) s | (a, b) `Set.member` s = removeDups' tl s
removeDups' ((a, b):tl) s | (b, a) `Set.member` s = removeDups' tl s
removeDups' ((a, b):tl) s = ((a, b):rest) where
s' = Set.insert (a, b) s
rest = removeDups' tl s'
removeDups :: Ord a => [(a, a)] -> [(a, a)]
removeDups l = removeDups' l (Set.fromList [])
The function removeDups calls the auxiliary function removeDups' with the list, and an empty set. For each pair, if it or its inverse are in the set, it is passed; otherwise, both it and its inverses are added, and the tail is processed. \
The complexity is O(n log(n)), as the size of the set is at most linear in n, at each step.
Example
...
main = do
putStrLn $ show $ removeDups [(1, 2), (1, 3), (2, 1)]
and
$ ghc ord.hs && ./ord
[1 of 1] Compiling Main ( ord.hs, ord.o )
Linking ord ...
[(1,2),(1,3)]
You can filter them using your own function:
checkEqTuple :: (a, b) -> (a, b) -> Bool
checkEqTuple (x, y) (x', y') | (x==y' && y == x') = True
| (x==x' && y == y') = True
| otherwise = False
then use nubBy
Prelude Data.List> nubBy checkEqTuple [(1,2), (2,1)]
[(1,2)]
I feel like I'm repeating myself a bit, but that's okay. None of this code had been tested or even compiled, so there may be bugs. Suppose we can impose an Ord constraint for efficiency. I'll start with a limited implementation of sets of pairs.
import qualified Data.Set as S
import qualified Data.Map.Strict as M
newtype PairSet a b =
PS (M.Map a (S.Set b))
empty :: PairSet a b
empty = PS M.empty
insert :: (Ord a, Ord b)
=> (a, b) -> PairSet a b -> PairSet a b
insert (a, b) (PS m) = PS $ M.insertWith S.union a (S.singleton b) m
member :: (Ord a, Ord b)
=> (a, b) -> PairSet a b -> Bool
member (a, b) (PS m) =
case M.lookup a m of
Nothing -> False
Just s -> S.member b s
Now we just need to keep track of which pairs we've seen.
order :: Ord a => (a, a) -> (a, a)
order p#(a, b)
| a <= b = p
| otherwise = (b, a)
nubSwaps :: Ord a => [(a,a)] -> [(a,a)]
nubSwaps xs = foldr go (`seq` []) xs empty where
go p r s
| member op s = r s
| otherwise = p : r (insert op s)
where op = order p
If a and b are ordered and compareable, you could just do this:
[(a,b) | (a,b) <- yourList, a<=b]