Consider a function, which takes a string and returns a list of all possible cases in which three subsequent 'X's can be removed from the list.
Example:
"ABXXXDGTJXXXDGXF" should become
["ABDGTJXXXDGXF", "ABXXXDGTJDGXF"]
(The order does not matter)
here is a naive implementation:
f :: String -> [String]
f xs = go [] xs [] where
go left (a:b:c:right) acc =
go (left ++ [a]) (b:c:right) y where -- (1)
y = if a == 'X' && b == 'X' && c == 'X'
then (left ++ right) : acc
else acc
go _ _ acc = acc
I think the main problem here is the line marked with (1). I'm constructing the left side of the list by appending to it, which is generally expensive.
Usually something like this can be solved by this pattern:
f [] = []
f (x:xs) = x : f xs
Or more explicitly:
f [] = []
f (x:right) = x : left where
left = f right
Now I'd have the lists right and left in each recursion. However, I need to accumulate them and I could not figure out how to do so here. Or am I on the wrong path?
A solution
Inspired by Gurkenglas' propose, here is a bit more generalized version of it:
import Data.Bool
removeOn :: (String -> Bool) -> Int -> String -> [String]
removeOn onF n xs = go xs where
go xs | length xs >= n =
bool id (right:) (onF mid) $
map (head mid:) $
go (tail xs)
where
(mid, right) = splitAt n xs
go _ = []
removeOn (and . map (=='X')) 3 "ABXXXDGTJXXXDGXF"
--> ["ABDGTJXXXDGXF","ABXXXDGTJDGXF"]
The main idea seems to be the following:
Traverse the list starting from its end. Make use of a 'look-ahead' mechanism which can examine the next n elements of the list (thus it must be checked, if the current list contains that many elements). By this recursive traversal an accumulating list of results is being enhanced in the cases the following elements pass a truth test. In any way those results must be added the current first element of the list because they stem from shorter lists. This can be done blindly, since adding characters to a result string won't change their property of being a match.
f :: String -> [String]
f (a:b:c:right)
= (if a == 'X' && b == 'X' && c == 'X' then (right:) else id)
$ map (a:) $ f (b:c:right)
f _ = []
Related
I try to find the number of elements that are the same in two lists. There are duplicate elements in two lists.
What I want:
-- (because there are two 's' in both lists )
duplicateEle "sssf" "ssah" = 2
-- (because there are two 'a' and one 's' in both lists, intotal 3 common elements)
duplicateEle "aass" "aaas" = 3
-- (because there are two 'a' and two 's' in both lists, intotal 4 common elements)
duplicateEle "ssaa" "ssaa" = 4
My strategy is check each element in List1 to see if it is the element in List2.
if each element of the List1 is the element of the List2.
If true, count 1 and delete (Data.List) the corresponding element in the second list.
For example,
input "dddd" "ssdd" output 2 because there are two d in both lists.
First I check if the 1st element in List1 which is d is an element in List2, the result is True, so I delete only one d in List2, count +1, now count is 1.
Then I check if the 2nd element in List1 which is d is an element in List2, the result is also True, so, I delete one d in List2,count +1, now count is 2.
Because there is not any d left in List2, so, the count will stay at 2.
My code is: (wrong)
import Data.List
duplicateEleCount :: [Char] -> [Char] -> Int
duplicateEleCount (x:xs) ys =
let count = if x `elem` ys then do 1 (delete x ys) else 0
in count + duplicateEleCount xs ys
What you wrote is not so Haskelly. Since it's strings, we can sort them, then group:
import Data.List
-- group :: Eq a => [a] -> [[a]] -- Defined in `Data.List'
dupreps :: String -> String -> Int
dupreps a b = r
where
x = group $ sort a
y = group $ sort b
Now we have them both ordered and grouped, we can just advance along the two lists in an obvious way,
r = merge'n'count x y 0
merge'n'count _ [] cnt = cnt
merge'n'count [] _ cnt = cnt
merge'n'count (g:gs) (f:fs) cnt
| head g == head f
= merge'n'count gs fs (cnt + min (length g) (length f))
| head g < head f
= merge'n'count gs (f:fs) cnt
| head g > head f
= merge'n'count (g:gs) fs cnt
So that we have e.g.
> dupreps "aab" "abbc"
2
> dupreps "aab" "aabbc"
3
> dupreps "aabccc" "bbc"
2
The groups g and f in merge'n'count are always non-empty by construction, so using head is OK.
If you just want to find the number of common elements between two lists which have repeated items, you can simply do this:
f x y = length $ nub $ intersect x y
intersect will find the common elements (with repetition*), and nub will get the distinct values from that list.
Note: intersect will only include repetition from the first argument i.e. intersect "ss" "s" will return "ss" but intersect "s" "ss" will return just "s".
EDIT: Based on the clarification, we can use foldl to get the desired outcome like so:
dup x y = fst $ foldl (\acc z -> if z `elem` (snd acc) then ((1 + fst acc), delete z (snd acc)) else acc) (0,y) x
This applies the strategy outlined in the question - if the element is found in current value of second list, increase the count and modify the second list, else do nothing.
I believe, this is what you intended to write?
import Data.List
duplicateEleCount :: [Char] -> [Char] -> Int
duplicateEleCount (x:xs) ys =
let (count, ys') = if x `elem` ys then (1, delete x ys) else (0, ys)
in count + duplicateEleCount xs ys'
duplicateEleCount [] _ = 0
You can't use do like you were trying to do. Remember that all variables in Haskell are immutable, so delete doesn't change the original list, it returns a new one that we will have to pass along to the recursive call.
A note on performance: this function is O(n*m), since we have to traverse the whole second list for every element in the first list. We can sort the lists first and perform something similar to the merge operation from merge sort to bring it down to O(n*log(n) + m*log(m))).
On another note, because of haskell's laziness, we can split the function up to one like this, without losing any performance and gaining flexibility:
import Data.List
duplicateElems :: [Char] -> [Char] -> [Char]
duplicateElems (x:xs) ys =
if x `elem` ys
then x : duplicateElems xs (delete x ys)
else duplicateElems xs ys
duplicateElems [] _ = []
duplicateEleCount xs ys = length $ duplicateElems xs ys
I'm trying to make it so that on a tuple input (n,m) and a list of tuples xs , if the first item in the tuple in xs is in (n,m) then keep it that way in the new list otherwise add the a tuple consisting of some value k from n to m as a first element and as second element it should be 0.My question is:how can i say "repeat 0" using guards ? Since clearly my code won't run since my code says "repeat = 0"
expand :: (Int,Int) -> Profile ->Profile
expand (n,m) [] = zip [n..m] (repeat 0)
expand (n,m) (x:xs) = zip [n..m] (repeat (|(fst (x) `elem` [n..m]) == False = 0
|otherwise = snd (x))
You can use a helper function here that converts a number in the [ n .. m ] range to a 2-tuple. Here we thus try to find an element in the list xs that matches with the first item of that tuple, if we do not find such element, we use 0:
import Data.List(find)
expand :: (Int,Int) -> Profile -> Profile
expand (n,m) xs = map go [n .. m]
where go i | Just l <- find (\(f, _) -> f == i) xs = l
| otherwise = (i, 0)
For a list, find was implemented as [src]:
find :: (a -> Bool) -> [a] -> Maybe a
find p = listToMaybe . filter p
filter thus will make a list that contains the elements that satisfy the predicate p, and listToMaybe :: [a] -> Maybe a will convert an empty list [] to Nothing, and for a non-empty list (x:_) it will wrap the first element x in a Just data constructor. Due to Haskell's laziness, it will thus look for the first element that satisfies the predicate.
this thus gives us:
Prelude Data.List> expand (2,7) [(4, 2.3), (6, 3)]
[(2,0.0),(3,0.0),(4,2.3),(5,0.0),(6,3.0),(7,0.0)]
This is a homework , it's to removing the adjacent duplicates.
The result should like this removeAdjacentDuplicates [3,1,2,2,2,2,2,4,4,2,2,3] == [3,1,2,4,2,3]
I knew it's not necessary to use head here, but it's not allowed to use rekursion and List-Comprehensions of Form [e | ...]. Only the function in Prelude is permittet, group und so on , which in other packages are also not allowed. map zip filter concat reverse foldr are recommended.
For example , It's not possible to make this:
removeAdjacentDuplicates :: Eq a => [a] -> [a]
removeAdjacentDuplicates (x:xs#(y:_))
| x == y = x:tail (removeAdjacentDuplicates xs)
| otherwise = x:removeAdjacentDuplicates xs
so I try like this
removeAdjacentDuplicates = foldr (\x result -> if ( x == (head result)) then result else (x : result)) []
but when I test it, it throw *** Exception: Prelude.head: empty list' here out
I have tried to add removeAdjacentDuplicates [] = [] before,
But error is like this
Equations for ‘removeAdjacentDuplicates’ have different numbers of arguments
H7-1.hs:24:1-32
H7-1.hs:25:1-105
|
24 | removeAdjacentDuplicates [] = []
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^...
I don't understand where the problem is, and how can I solve it?
x == head result dies if result is [] -- and result is definitely [] in the first iteration of foldr, so adding a special case for when the input list would not require foldr to do any iterations is fixing exactly the wrong case!
Instead of trying to extract a value from the result list, you could insert x into a list; so consider using the condition
[x] == take 1 result
instead -- it never dies.
As said by #DanielWagner, head :: [a] -> a will raise an error for empty lists. We can solve this by using take :: Int -> [a] -> [a], or by using pattern matching:
removeAdjacentDuplicates :: (Foldable f, Eq a) => f a -> [a]
removeAdjacentDuplicates = foldr f []
where f x ys#(y:_) | x == y = ys
f x ys = (x:ys)
here ys#(y:_) will match given the list is non-empty, with y as head of the list. In that case we thus check if x == y, and if that holds, we return ys. Otherwise we return (x:ys).
how can i get the most frequent value in a list example:
[1,3,4,5,6,6] -> output 6
[1,3,1,5] -> output 1
Im trying to get it by my own functions but i cant achieve it can you guys help me?
my code:
del x [] = []
del x (y:ys) = if x /= y
then y:del x y
else del x ys
obj x []= []
obj x (y:ys) = if x== y then y:obj x y else(obj x ys)
tam [] = 0
tam (x:y) = 1+tam y
fun (n1:[]) (n:[]) [] =n1
fun (n1:[]) (n:[]) (x:s) =if (tam(obj x (x:s)))>n then fun (x:[]) ((tam(obj x (x:s))):[]) (del x (x:s)) else(fun (n1:[]) (n:[]) (del x (x:s)))
rep (x:s) = fun (x:[]) ((tam(obj x (x:s))):[]) (del x (x:s))
Expanding on Satvik's last suggestion, you can use (&&&) :: (b -> c) -> (b -> c') -> (b -> (c, c')) from Control.Arrow (Note that I substituted a = (->) in that type signature for simplicity) to cleanly perform a decorate-sort-undecorate transform.
mostCommon list = fst . maximumBy (compare `on` snd) $ elemCount
where elemCount = map (head &&& length) . group . sort $ list
The head &&& length function has type [b] -> (b, Int). It converts a list into a tuple of its first element and its length, so when it is combined with group . sort you get a list of each distinct value in the list along with the number of times it occurred.
Also, you should think about what happens when you call mostCommon []. Clearly there is no sensible value, since there is no element at all. As it stands, all the solutions proposed (including mine) just fail on an empty list, which is not good Haskell. The normal thing to do would be to return a Maybe a, where Nothing indicates an error (in this case, an empty list) and Just a represents a "real" return value. e.g.
mostCommon :: Ord a => [a] -> Maybe a
mostCommon [] = Nothing
mostCommon list = Just ... -- your implementation here
This is much nicer, as partial functions (functions that are undefined for some input values) are horrible from a code-safety point of view. You can manipulate Maybe values using pattern matching (matching on Nothing and Just x) and the functions in Data.Maybe (preferable fromMaybe and maybe rather than fromJust).
In case you would like to get some ideas from code that does what you wish to achieve, here is an example:
import Data.List (nub, maximumBy)
import Data.Function (on)
mostCommonElem list = fst $ maximumBy (compare `on` snd) elemCounts where
elemCounts = nub [(element, count) | element <- list, let count = length (filter (==element) list)]
Here are few suggestions
del can be implemented using filter rather than writing your own recursion. In your definition there was a mistake, you needed to give ys and not y while deleting.
del x = filter (/=x)
obj is similar to del with different filter function. Similarly here in your definition you need to give ys and not y in obj.
obj x = filter (==x)
tam is just length function
-- tam = length
You don't need to keep a list for n1 and n. I have also made your code more readable, although I have not made any changes to your algorithm.
fun n1 n [] =n1
fun n1 n xs#(x:s) | length (obj x xs) > n = fun x (length $ obj x xs) (del x xs)
| otherwise = fun n1 n $ del x xs
rep xs#(x:s) = fun x (length $ obj x xs) (del x xs)
Another way, not very optimal but much more readable is
import Data.List
import Data.Ord
rep :: Ord a => [a] -> a
rep = head . head . sortBy (flip $ comparing length) . group . sort
I will try to explain in short what this code is doing. You need to find the most frequent element of the list so the first idea that should come to mind is to find frequency of all the elements. Now group is a function which combines adjacent similar elements.
> group [1,2,2,3,3,3,1,2,4]
[[1],[2,2],[3,3,3],[1],[2],[4]]
So I have used sort to bring elements which are same adjacent to each other
> sort [1,2,2,3,3,3,1,2,4]
[1,1,2,2,2,3,3,3,4]
> group . sort $ [1,2,2,3,3,3,1,2,4]
[[1,1],[2,2,2],[3,3,3],[4]]
Finding element with the maximum frequency just reduces to finding the sublist with largest number of elements. Here comes the function sortBy with which you can sort based on given comparing function. So basically I have sorted on length of the sublists (The flip is just to make the sorting descending rather than ascending).
> sortBy (flip $ comparing length) . group . sort $ [1,2,2,3,3,3,1,2,4]
[[2,2,2],[3,3,3],[1,1],[4]]
Now you can just take head two times to get the element with the largest frequency.
Let's assume you already have argmax function. You can write
your own or even better, you can reuse list-extras package. I strongly suggest you
to take a look at the package anyway.
Then, it's quite easy:
import Data.List.Extras.Argmax ( argmax )
-- >> mostFrequent [3,1,2,3,2,3]
-- 3
mostFrequent xs = argmax f xs
where f x = length $ filter (==x) xs
I've seen the other threads about missing split function but I didn't want to peek as I do this for learning purposes and want to figure out myself. So here it is:
split :: Char -> String -> [String]
split c xs | null f = []
| otherwise = f : split c s'
where (f,s) = break (== c) xs
s' | null s = s
| otherwise = tail s
It seems to work fine (please tell me if anything is wrong with it) but when I use a splitting character that is not in the string then the function returns a list with a single element of the original string whereas I want it to return an empty list. I can't figure out how to do it.
Any ideas?.
You can simply write a wrapper function, changing the result of your original one:
split' x xs = go (split x xs) where
go [_] = []
go ys = ys
There are many ways to write a split function, e.g.
split x xs = foldl' go [[]] xs where
go (ys:yss) y | y == x = []:ys:yss
| otherwise = (y:ys):yss
or
import Data.List
split x xs = map tail $ groupBy (const (/=x)) (x:xs)