I am having trouble with an assignment question!
Write the function
freq2 :: String -> -> [(Int,[Char])]
Like freq, the function freq2 counts frequency of occurrence of alphabetic characters.
Given the string:
We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness
I need to end up with:
[(1,"qv"), (2,"gm"), (3,"cfpwy"), (4,"b"), (5,"u"), (6,"do"),(8,"s"), (9,"ln"), (10,"i"), (12,"r"), (13,"h"), (16,"a"),(22,"t"), (28,"e")]
So far I can get to:
[('q',1),('v',1),('g',2),('m',2),('c',3),('f',3),('p',3),('w',3),('y',3),('b',4),('u',5),('d',6),('o',6),('s',8),('l',9),('n',9),('i',10),('r',12),('h',13),('a',16),('t',22),('e',28)]
Using:
freq2 :: String -> [(Char,Int)]
freq2 input = result2
where
lower_case_list = L.map C.toLower input
filtered_list = L.filter C.isAlpha lower_case_list
result = L.map (\a -> (L.head a, L.length a)) $ L.group $ sort filtered_list
result2 = sortBy (compare `on` snd) result
Is there an easy way to get to the last stage or to do the whole thing, possibly using library functions? Or can you please provide some direction on how to finish off this question?
Thanks
Something like this appended to your solution should work:
result3 = map (\xs#((_,x):_) -> (x, map fst xs)) $ L.groupBy ((==) `on` snd) result2
My preference would be to use a Map for these types of problems though:
import qualified Data.Map as Map
import qualified Data.Char as C
import qualified Data.Tuple as T
string = filter C.isAlpha $ map C.toLower "We hold these truths to be self-evident, that all men are created equal, that they are endowed by their Creator with certain unalienable Rights, that among these are Life, Liberty and the pursuit of Happiness"
swapMapWith f = Map.fromListWith f . map T.swap . Map.toList
freq2 :: String -> [(Int, String)]
freq2 = Map.toList . swapMapWith (++) . foldl (\agg c -> Map.insertWith (+) [c] 1 agg) Map.empty
Method 1:
import needed modules
import Data.Char
import Data.List
filter out uninterested characters and convert the rest to lower case
toLowerAlpha :: String -> String
toLowerAlpha = map toLower . filter isAlpha
sort first, then group, after that the length of each group is the frequency of character in that group
elemFreq :: (Ord a) => [a] -> [(Int, a)]
elemFreq = map (\l -> (length l, head l)) . group . sort
sort and group as step 2, but according to frequency at here, then combine all those characters that have the same frequencies
groupByFreq :: (Integral a, Ord b) => [(a, b)] -> [[(a, b)]]
groupByFreq = groupBy (onFreq (==)) . sortBy (onFreq compare)
where onFreq op (f1,_) (f2,_) = op f1 f2
collectByFreq :: (Integral a) => [[(a, b)]] -> [(a, [b])]
collectByFreq = map (\ls -> (fst . head $ ls, map snd ls))
sequence the above functions will give the required function
freq2 = collectByFreq . groupByFreq . elemFreq . toLowerAlpha
Method 2:
import needed modules
import qualified Data.Char as Char
import qualified Data.Map as Map
filter out uninterested characters and convert the rest to lower case
toLowerAlpha :: String -> String
toLowerAlpha = map Char.toLower . filter Char.isAlpha
create a map, key and value are character and corresponding frequency, respectively
toFreqMap :: (Ord a, Num b) => [a] -> Map.Map a b
toFreqMap = foldr (\c -> Map.insertWith (+) c 1) Map.empty
convert the map created in step 2 to another map, using frequency as key, and characters have that frequency as value
toFreqCol :: (Ord a, Ord b) => Map.Map a b -> Map.Map b [a]
toFreqCol = Map.foldrWithKey (\k a m -> Map.insertWith (++) a [k] m) Map.empty
sequence the above functions will give the required function
freq2 = Map.toAscList . toFreqCol . toFreqMap . toLowerAlpha
Related
The task is as follows: I have a list of tuples containing country names, and the official language. I have another list with "3 item tuples" of translators in (first name, last name, language) format. I need to get a list of the countries, which official language is NOT spoken by any of the translators.
It has to be one call to a function of such form:
foo :: [(String, String)] -> [(String, String, String)] -> [String]
I have tried all sorts of map and filter combinations to no avail. The closest I came was using sets.
third :: (a, b, c) -> c
third (_, _, z) = z
translators x = Set.fromList (map third x)
languages x = Set.fromList (map snd x)
diff x y = Set.toList (Set.difference (languages x) (translators y))
This gives me all the languages that are in the first list but not spoken by the translators. However of course, the task is to give a list of the country names, not the languages. So I tried this as well, but it does not work:
foo x y = filter ((Set.notMember (translators y)).snd) x
I'm a beginner and I would much appreciate some help.
Your first function gives you the correct answer - except it gives you the list of languages. How about an assoc list so you can lookup country associated with the language?
To do that, you just have to swap the country and language in the (Country, Language) tuple
import Data.Tuple ( swap )
langMap :: [(String, String)] -> [(String, String)]
langMap = map swap
Now once you have the result of your own diff function, you just have to map a lookup function over the resulting list
diff :: [(String, String)] -> [(String, String, String)] -> [String]
diff x y = map (fromJust . (`lookup` langMap')) diffList
where
diffList = Set.toList $ Set.difference (languages x) (translators y)
langMap' = langMap x
If you are concerned about performance, you can replace the assoc list by a Data.Map
import qualified Data.Map as Map
langMap :: [(String, String)] -> Map.Map String String
langMap = Map.fromList . map swap
And also change the lookup in diff to Map.lookup.
diff :: [(String, String)] -> [(String, String, String)] -> [String]
diff x y = map (fromJust . (`Map.lookup` langMap')) diffList
where
diffList = Set.toList $ Set.difference (languages x) (translators y)
langMap' = langMap x
However, there's one caveat here - the lookup method will only work if you have only one country associated with each language.
If you want to support the situation when there are multiple countries associated with the same language. You need some extra batteries on the langMap-
langMap :: [(String, String)] -> Map.Map String [String]
langMap x = Map.fromListWith (++) [(v, [k]) | (k, v) <- x]
This is a slight modification of this answer. It creates a map of String and [String]. The key string is the language, and the value is a list of countries
Now, since the value of the map is a list of strings instead of just a string - we also need to change the diff a bit
diff :: [(String, String)] -> [(String, String)] -> [String]
diff x y = concatMap (fromJust . (`Map.lookup` langMap')) diffList
where
diffList = Set.toList $ Set.difference (languages x) (translators y)
langMap' = langMap x
Huh, that was simple. Just changing map to concatMap is enough - it'll flatten the list of list of strings, into just a list of strings.
Let's see it in action-
λ> xs = [("US", "English"), ("Mexico", "Spanish"), ("France", "French"), ("Spain", "Spanish"), ("UK", "English"), ("Italy", "Italian")]
λ> ys = [("Foo", "Spanish"), ("Bar", "French")]
λ> diff xs ys
["UK","US","Italy"]
Let's focus on correctness, on solving the problem, not on the solution being an efficient one. That we can take care of later. Correctness first, efficiency later!
Simplest and most visual way to code this is using List Comprehensions.
foo :: [(String, String)] -> [(String, String, String)] -> [String]
-- Country Language FirstN LastN Language
-- return: list of Contries with no translator
foo cls nnls = [ c | (c, ___) <- cls, -- for each country/language pair,
-- test the {list of ()s for each translator that ...}
___ [ () | (_, _, ___) <- nnls, ___ == ___] ]
Fill in the ___ blanks (you don't have to fill the _ blanks, those are Haskell's anonymous, throwaway variables).
Now you can make it efficient, treating the above as executable specification.
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.
I am trying to convert a list to a data.map so that from a list like:
mylist = ["one","one","two","three","two","two","three","four","four","four","four","five","five","two"]
I get something like:
("one", 2) ("two", 4) ...
I am trying following code:
import qualified Data.Map as Map
import Data.List
list2dict [] mymap = print mymap
list2dict [y:ys] mymap = do
if (Map.lookup y mymap) /= Nothing
then list2dict [ys] $ Map.insert y ((Map.lookup y) + 1) mymap
else list2dict [ys] $ Map.insert y 1 mymap
mylist = ["one","one","two","three","two","two","three","four","four","four","four","five","five","two"]
main = do
list2dict (sort mylist) $ Map.empty
However, I am getting following error:
soq_list2dict.hs:5:1: error:
• Non type-variable argument
in the constraint: Show (Map.Map k a -> Maybe a)
(Use FlexibleContexts to permit this)
• When checking the inferred type
list2dict :: forall a k.
(Show (Map.Map k a -> Maybe a), Show k, Ord k,
Num (Map.Map k a -> Maybe a), Eq (Map.Map k a -> Maybe a)) =>
[[k]] -> Map.Map k (Map.Map k a -> Maybe a) -> IO ()
How can this be solved?
Edit: using (y:ys) instead of [y:ys] gives following error:
soq_list2dict.hs:5:1: error:
• Occurs check: cannot construct the infinite type: k ~ [k]
Expected type: [[k]] -> Map.Map k (Map.Map k a -> Maybe a) -> IO ()
Actual type: [k] -> Map.Map k (Map.Map k a -> Maybe a) -> IO ()
• Relevant bindings include
list2dict :: [[k]] -> Map.Map k (Map.Map k a -> Maybe a) -> IO ()
(bound at soq_list2dict.hs:5:1)
Willem Van Onsem already pointed out the first problem in a comment: you used [y:ys] instead of (y:ys). The second problem is that you used [ys] instead of just ys in two places. The third problem is that you say ((Map.lookup y) + 1), which creates a nonsensical type. (Even if you had used ((Map.lookup y mymap) + 1) instead, which is closer to correct, you still would have just gotten a different error.) This way will work instead:
list2dict (y:ys) mymap = case Map.lookup y mymap of
Just x -> list2dict ys $ Map.insert y (x + 1) mymap
Nothing -> list2dict ys $ Map.insert y 1 mymap
Note that I pattern-match on the Maybe rather than testing it with if and then trying to extract the value separately after that.
You made a classic error: a non-empty list has as pattern (x:xs) (notice the empty brackets). But you make things too complicated here.
You can implement this with a foldr pattern, that can convert any (Ord a, Foldable f) => f a to a (Ord a, Integral i) => Map a i:
import Data.Map(Map, alter, empty)
import Data.Maybe(maybe)
toCounter :: (Ord a, Foldable f, Integral i) => f a -> Map a i
toCounter = foldr (alter (Just . maybe 1 (1+))) empty
We thus start with an empty map, and for each item in the Foldable, we perform an alter where, in case the item already exists (then it gives us a Just n, we return a Just (n+1), and for a Nothing we fallback on 1.
Since it works on (Ord a, Foldable f) => f a, you can make a "counter" for anything of a type that is an instance of the Ord typeclass, and is stored in a object of a type that is an instance of a Foldable. So it can count items in a list, Maybe (well this has exactly one or no item), a Tree, etc.
For example:
*Main> toCounter ["one","one","two","three","two","two","three","four","four","four","four","five","five","two"]
fromList [("five",2),("four",4),("one",2),("three",2),("two",4)]
A shorter version, that works on lists is one written by #DavidFletcher:
import Data.Map(Map, fromListWith)
toCounter :: (Ord a, Integral i) => [a] -> Map a i
toCounter = fromListWith (+) . map (flip (,) 1)
we can however use toList to let it work with Foldables as well:
import Data.Foldable(toList)
import Data.Map(Map, fromListWith)
toCounter :: (Ord a, Foldable f, Integral i) => f a -> Map a i
toCounter = fromListWith (+) . map (flip (,) 1) . toList
I've got problem with a Haskell program.
I'm trying to change [[Char]] to [[Int]]
I've got
["2","2","1","2,2","1"]
list of char list
and I'm trying to change it to [[Int]]
[[2],[2],[1],[2,2],[1]]
I've tried
f :: [String] -> [Int]
f = map read
but it gives me
[2,2,1,*** Exception: Prelude.read: no parse
Can anybody help me with this?
The reason that this fails is because a string "2,2" can not be converted to an Int itself: this is a digit followed by a comma, followed by a digit. An Int is parsed by an optional minus sign, followed by some digits, and some extra possibilities like hexadecimal numbers, but let us ignore these for now.
The type signature you specify for f is however incorrect, based on the expected output. Your output type seems to be a list of lists of Ints, so [[Int]]. That means that you should specify f as:
f :: [String] -> [[Int]]
f = ...
We thus need to read every String to an [Int]. We can not use read directly here, since reading to an [Int] expects the string to start and end with square brackets. We can however add these manually like:
f :: [String] -> [[Int]]
f = map (\s -> read ('[' : s ++ "]"))
or a point-free version:
f :: [String] -> [[Int]]
f = map (read . ('[' :) . (++ "]"))
For example:
Prelude> f ["2","2","1","2,2","1"]
[[2],[2],[1],[2,2],[1]]
Towards safer parsing with readMaybe
Parsing from Strings like in the above way is of course not very "safe", since it is possible that the String does not follow the format. We can make this more safe and use for example readMaybe :: Read a => String -> Maybe a:
import Text.Read(readMaybe)
f :: [String] -> [Maybe [Int]]
f = map (readMaybe . ('[' :) . (++ "]"))
For example:
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[Just [2],Nothing,Just [4,7,3],Nothing]
we can omit the failed reads for example by using catMaybes :: [Maybe a] -> [a]:
import Data.Maybe(catMaybes)
import Text.Read(readMaybe)
f :: [String] -> [[Int]]
f = catMaybes . map (readMaybe . ('[' :) . (++ "]"))
For example:
Prelude Data.Maybe Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[[2],[4,7,3]]
or as #dfeuer said, we can use traverse :: (Applicative f, Traversable t) => (a -> f b) -> t a -> f (t b) to return an [[Int]] result wrapped in a Just if all parsing succeeded, and Nothing otherwise:
import Text.Read(readMaybe)
f :: [String] -> Maybe [[Int]]
f = traverse (readMaybe . ('[' :) . (++ "]"))
For example:
Prelude Text.Read> f ["2","2","1","2,2","1"]
Just [[2],[2],[1],[2,2],[1]]
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
Nothing
Parse with error messages with readEither
We can obtain an error message wrapped in a Left in case the parsing fails by using readEither :: Read a => String -> Either String a:
import Text.Read(readEither)
f :: [String] -> [Either String [Int]]
f = map (readEither . ('[' :) . (++ "]"))
For example:
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
[Right [2],Left "Prelude.read: no parse",Right [4,7,3],Left "Prelude.read: no parse"]
and use traverse in the same way to obtain an error message wrapped in a Left or the complete result in a Right:
import Text.Read(readEither)
f :: [String] -> Either String [[Int]]
f = traverse (readEither . ('[' :) . (++ "]"))
For example:
Prelude Text.Read> f ["2","2","1","2,2","1"]
Right [[2],[2],[1],[2,2],[1]]
Prelude Text.Read> f ["2", "3;2", "4,7,3", "bla"]
Left "Prelude.read: no parse"
Here, like #dfeuer says, it does not really shows much information. There are however parsers that can provide more informative parsing errors.
The next lines should show how its has to work..
[14,2,344,41,5,666] after [(14,2),(2,1),(344,3),(5,1),(666,3)]
["Zoo","School","Net"] after [("Zoo",3),("School",6),("Net",3)]
Thats my code up to now
zipWithLength :: [a] -> [(a, Int)]
zipWithLength (x:xs) = zipWith (\acc x -> (x, length x):acc) [] xs
I want to figure out what the problem in the second line is.
If you transform the numbers into strings (using show), you can apply length on them:
Prelude> let zipWithLength = map (\x -> (x, length (show x)))
Prelude> zipWithLength [14,2,344,41,5,666]
[(14,2),(2,1),(344,3),(41,2),(5,1),(666,3)]
However, you cannot use the same function on a list of strings:
Prelude> zipWithLength ["Zoo","School","Net"]
[("Zoo",5),("School",8),("Net",5)]
The numbers are not the lengths of the strings, but of their representations:
Prelude> show "Zoo"
"\"Zoo\""
Prelude> length (show "Zoo")
5
As noted in the comments, similar problems may happen with other types of elements:
Prelude> zipWithLength [(1.0,3),(2.5,3)]
[((1.0,3),7),((2.5,3),7)]
Prelude> show (1.0,3)
"(1.0,3)"
Prelude> length (show (1.0,3))
7
If you want to apply a function on every element of a list, that is a map :: (a -> b) -> [a] -> [b]. The map thus takes a function f and a list xs, and generates a list ys, such that the i-th element of ys, is f applied to the i-th element of xs.
So now the only question is what mapping function we want. We want to take an element x, and return a 2-tuple (x, length x), we can express this with a lambda expression:
mapwithlength = map (\x -> (x, length x))
Or we can use ap :: Monad m => m (a -> b) -> m a -> m b for that:
import Control.Monad(ap)
mapwithlength = map (ap (,) length)
A problem is that this does not work for Ints, since these have no length. We can use show here, but there is an extra problem with that: if we perform show on a String, we get a string literal (this means that we get a string that has quotation marks, and where some characters are escaped). Based on the question, we do not want that.
We can define a parameterized function for that like:
mapwithlength f = map (ap (,) (length . f))
We can basically leave it to the user. In case they want to work with integers, they have to call it with:
forintegers = mapwithlength show
and for Strings:
forstrings = mapwithlength id
After installing the number-length package, you can do:
module Test where
import Data.NumberLength
-- use e.g for list of String
withLength :: [[a]] -> [([a], Int)]
withLength = map (\x -> (x, length x))
-- use e.g for list of Int
withLength' :: NumberLength a => [a] -> [(a, Int)]
withLength' = map (\x -> (x, numberLength x))
Examples:
>>> withLength ["Zoo", "bear"]
[("Zoo",3),("bear",4)]
>>> withLength' [14, 344]
[(14,2),(344,3)]
As bli points out, calculating the length of a number using length (show n) does not transfer to calculating the length of a string, since show "foo" becomes "\"foo\"". Since it is not obvious what the length of something is, you could parameterise the zip function with a length function:
zipWithLength :: (a -> Int) -> [a] -> [(a, Int)]
zipWithLength len = map (\x -> (x, len x))
Examples of use:
> zipWithLength (length . show) [7,13,666]
[(7,1),(13,2),(666,3)]
> zipWithLength length ["Zoo", "School", "Bear"]
[("Zoo",3),("School",6),("Bear",4)]
> zipWithLength (length . concat) [[[1,2],[3],[4,5,6,7]], [[],[],[6],[6,6]]]
[([[1,2],[3,4],[5,6,7]],7),([[],[],[6],[6,6]],3)]