Log in

View Full Version : haskell help, again



scarface
10-23-2008, 08:10 PM
hi all again from me, the haskell newbie. I again need some help with haskell and one exercise. It basically states to count the number of times each word occurs in a string. What i am doing is I split the original string to a list of many strings(using the words function) but then i get stuck on how to count the number of times each string occurs. I tried to use the length function and also tried to modify it so it returns number of times each string occurs in the list instead of characters but no luck. any help is much appreciated

thnx in advance

Trinithis
10-24-2008, 05:01 AM
Not sure if this is a little much, but try understanding this:



import Data.List (foldl')
import qualified Data.Map as M
import Data.Char

countWords = M.toList . foldl' count M.empty . words
where
count m wd = M.alter (
\key -> Just $ case key of
Nothing -> 1
Just n -> n + 1
) (normalize wd) m
normalize = map toLower . filter (not . flip elem ",.?!;:'\"")




*Main> countWords "The man in the red suit is the man in the kitchen."
[("in",2),("is",1),("kitchen",1),("man",2),("red",1),("suit",1),("the",4)]


Also, if you don't know about it, http://haskell.org/hoogle/ is a great resource. Same with #haskell on irc freenode.

Twey
10-24-2008, 05:54 AM
Well, the main advantage of using a map here is the ability to return a map. If you're not returning a map, you might as well do:
import Data.List (sort, group)
import Data.Char (toLower, isAlphaNum)

count = map (\n -> (head n, length n)) . group . sort

countWords = count . map (map toLower . filter isAlphaNum) . wordsI would prefer to use Map for this one, though:
import Data.List (sort, group)
import Data.Char (toLower, isAlphaNum)
import Data.Map (fromList)

count = map (\n -> (head n, length n)) . group . sort

countWords = Map.fromList . count . map (map toLower . filter isAlphaNum) . words

Trinithis
10-24-2008, 06:07 AM
Oh, group is a cool function. Completely forgot about that beauty, though in your second example you make no use of it being a map. Or am I missing the point?

Also, the following is better than my original:


countWords = foldl' (
\m wd -> M.insertWith (+) (map toLower $ filter isAlphaNum wd) 1 m
) M.empty . words

scarface
10-25-2008, 11:07 AM
hi again, i was wondering if i can use a function i have defined in the place of (map toLower..) instead of map toLower can i have something like map minimize if i have defined minimize to put all letters to lower case?

Twey
10-25-2008, 12:12 PM
though in your second example you make no use of it being a map. Or am I missing the point?You are. The point is to return a map for the calling code to use. It is possible to build it up as a map as you did, but not really necessary — there's no significant advantage, since it's necessary to go through the list anyway, and it just makes it messier and more complicated.
hi again, i was wondering if i can use a function i have defined in the place of (map toLower..) instead of map toLower can i have something like map minimize if i have defined minimize to put all letters to lower case?You can, but note that minimize = toLower, so there's not really much point (and the standard implementation is almost certainly better than yours).

scarface
10-25-2008, 04:02 PM
ok thanx Twey..i will just stick to toLower then..1 more thing, i tried out Trinithis' code and it works great..after that i went to try and include the code Twey posted earlier "orderWords = unwords . sort . words" thinking it would first count the words and then sort them but that not what quite happened.I noticed that i can add the sort function at the end of Trinithis's code but the unwords one gives me error. I dont quite understand why that is.

countWords = foldl (
\m wd -> Map.insertWith (+) (map toLower $ filter isAlphaNum wd) 1 m
) Map.empty . unwords. sort. words

this is my hacky haskell code that doesnt work

Trinithis
10-25-2008, 06:52 PM
In your other thread, I mentioned that you should put spaces on either side of (.) (composition). The main reason is that Haskell uses the same character for qualifying a function (eg: Map.empty), and you want to distinguish composition and qualification in your code.

Anyway...

What you want is:


countWords = foldl (
\m wd -> Map.insertWith (+) (map toLower $ filter isAlphaNum wd) 1 m
) Map.empty . sort . words


But maps order their keys (ie: the words), so the sort is not necessary; it gets done automatically. Exactly same as original.


countWords = foldl (
\m wd -> Map.insertWith (+) (map toLower $ filter isAlphaNum wd) 1 m
) Map.empty . words


Twey's code needs to have a words in it to work:


count = map (\n -> (head n, length n)) . group . sort

countWords = Map.fromList . count . map (map toLower . filter isAlphaNum) . words



import Control.Arrow (Arrow((&&&)))

count = Map.fromList . map (head &&& length) . group . sort

countWords = count . map (map toLower . filter isAlphaNum) . words

Twey
10-25-2008, 11:53 PM
Whoops, you're right. Added that :)