Results 1 to 9 of 9

Thread: haskell help, again

  1. #1
    Join Date
    Oct 2008
    Posts
    20
    Thanks
    0
    Thanked 2 Times in 2 Posts

    Default haskell help, again

    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

  2. #2
    Join Date
    May 2007
    Location
    USA
    Posts
    373
    Thanks
    2
    Thanked 4 Times in 4 Posts

    Default

    Not sure if this is a little much, but try understanding this:

    Code:
    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 ",.?!;:'\"")
    Code:
    *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.
    Trinithis

  3. #3
    Join Date
    Jun 2005
    Location
    英国
    Posts
    11,876
    Thanks
    1
    Thanked 180 Times in 172 Posts
    Blog Entries
    2

    Default

    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:
    Code:
    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) . words
    I would prefer to use Map for this one, though:
    Code:
    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
    Last edited by Twey; 10-25-2008 at 10:02 PM. Reason: Added missing 'words'.
    Twey | I understand English | 日本語が分かります | mi jimpe fi le jbobau | mi esperanton komprenas | je comprends franšais | entiendo espa˝ol | t˘i Ýt hiểu tiếng Việt | ich verstehe ein bisschen Deutsch | beware XHTML | common coding mistakes | tutorials | various stuff | argh PHP!

  4. #4
    Join Date
    May 2007
    Location
    USA
    Posts
    373
    Thanks
    2
    Thanked 4 Times in 4 Posts

    Default

    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:
    Code:
    countWords = foldl' (
      \m wd ->  M.insertWith (+) (map toLower $ filter isAlphaNum wd) 1 m
      ) M.empty . words
    Last edited by Trinithis; 10-24-2008 at 06:19 AM.
    Trinithis

  5. #5
    Join Date
    Oct 2008
    Posts
    20
    Thanks
    0
    Thanked 2 Times in 2 Posts

    Default

    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?

  6. #6
    Join Date
    Jun 2005
    Location
    英国
    Posts
    11,876
    Thanks
    1
    Thanked 180 Times in 172 Posts
    Blog Entries
    2

    Default

    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).
    Twey | I understand English | 日本語が分かります | mi jimpe fi le jbobau | mi esperanton komprenas | je comprends franšais | entiendo espa˝ol | t˘i Ýt hiểu tiếng Việt | ich verstehe ein bisschen Deutsch | beware XHTML | common coding mistakes | tutorials | various stuff | argh PHP!

  7. #7
    Join Date
    Oct 2008
    Posts
    20
    Thanks
    0
    Thanked 2 Times in 2 Posts

    Default

    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

  8. #8
    Join Date
    May 2007
    Location
    USA
    Posts
    373
    Thanks
    2
    Thanked 4 Times in 4 Posts

    Default

    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:
    Code:
    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.
    Code:
    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:
    Code:
    count = map (\n -> (head n, length n)) . group . sort
    
    countWords = Map.fromList . count . map (map toLower . filter isAlphaNum) . words
    Code:
    import Control.Arrow (Arrow((&&&)))
    
    count = Map.fromList . map (head &&& length) . group . sort
    
    countWords = count . map (map toLower . filter isAlphaNum) . words
    Last edited by Trinithis; 10-25-2008 at 07:02 PM.
    Trinithis

  9. #9
    Join Date
    Jun 2005
    Location
    英国
    Posts
    11,876
    Thanks
    1
    Thanked 180 Times in 172 Posts
    Blog Entries
    2

    Default

    Whoops, you're right. Added that
    Twey | I understand English | 日本語が分かります | mi jimpe fi le jbobau | mi esperanton komprenas | je comprends franšais | entiendo espa˝ol | t˘i Ýt hiểu tiếng Việt | ich verstehe ein bisschen Deutsch | beware XHTML | common coding mistakes | tutorials | various stuff | argh PHP!

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •