Counting Fruits

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

Counting Fruits

builes.adolfo@googlemail.com
Hello everyone ,
I wanted to do a small program which read a txt with fruit's name in each
line and then print how many fruits I have of each type. something like
these:
  apple
  apple

and then

[(apple,2)]

I came up whit this

import qualified Data.Map as Map
import Data.List
import System.IO

main =
    do
      file <- readFile "fruits.txt"
      let answer = proccessFile $ lines file

      putStrLn (show answer)

proccessFile :: [String] -> [(String,Int)]
proccessFile file = Map.toAscList $ parseFile Map.empty  file
    where parseFile fruits [] = fruits
          parseFile fruits_map (x:xs) = parseFile (Map.insertWith (+)
x 1 fruits_map) xs


It works, but I would like to know how would  you do it ?,  Share different
points of view, different code. Was it a good idea to use a Map  ?, Did I
separate the code  in a proper way, I mean pure - impure ? How can we
improve the performance ?

Best Regards for everyone.


Adolfo
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.haskell.org/pipermail/beginners/attachments/20090219/1162574a/attachment.htm
Reply | Threaded
Open this post in threaded view
|

Counting Fruits

Alexander Dunlap
 On Thu, Feb 19, 2009 at 3:15 PM, Adolfo Builes
<[hidden email]> wrote:

> Hello everyone ,
> I wanted to do a small program which read a txt with fruit's name in each
> line and then print how many fruits I have of each type. something like
> these:
>   apple
>   apple
>
> and then
>
> [(apple,2)]
>
> I came up whit this
>
> import qualified Data.Map as Map
> import Data.List
> import System.IO
>
> main =
>     do
>       file <- readFile "fruits.txt"
>       let answer = proccessFile $ lines file
>
>
>       putStrLn (show answer)
>
> proccessFile :: [String] -> [(String,Int)]
> proccessFile file = Map.toAscList $ parseFile Map.empty  file
>     where parseFile fruits [] = fruits
>           parseFile fruits_map (x:xs) = parseFile (Map.insertWith (+) x 1
> fruits_map) xs
>
>
> It works, but I would like to know how would  you do it ?,  Share different
> points of view, different code. Was it a good idea to use a Map  ?, Did I
> separate the code  in a proper way, I mean pure - impure ? How can we
> improve the performance ?
>
> Best Regards for everyone.
>
>
> Adolfo

You spelled "process" incorrectly ;). Not a big deal of course, just
makes your code a bit cleaner-looking.

In terms of actual code, I think "proccessFruits" could probably be
rewritten more clearly (and perhaps more efficiently, due to rewrite
rules) using a fold. You might also check out the "group" function
from Data.List, although that approach would probably be a bit slower.

Hope that helps.

Alex
Reply | Threaded
Open this post in threaded view
|

Re: Counting Fruits

Heinrich Apfelmus
In reply to this post by builes.adolfo@googlemail.com
Adolfo Builes wrote:

>
> I wanted to do a small program which read a txt with fruit's name in each
> line and then print how many fruits I have of each type. something like
> these:
>   apple
>   apple
>
> and then
>
> [(apple,2)]
>
> I came up with this
>
> import qualified Data.Map as Map
> import Data.List
> import System.IO
>
> main =
>     do
>       file <- readFile "fruits.txt"
>       let answer = proccessFile $ lines file
>
>       putStrLn (show answer)
>
> proccessFile :: [String] -> [(String,Int)]
> proccessFile file = Map.toAscList $ parseFile Map.empty  file
>     where parseFile fruits [] = fruits
>           parseFile fruits_map (x:xs) = parseFile (Map.insertWith (+)
> x 1 fruits_map) xs
>
>
> It works, but I would like to know how would  you do it ?,  Share different
> points of view, different code. Was it a good idea to use a Map  ?, Did I
> separate the code  in a proper way, I mean pure - impure ? How can we
> improve the performance ?

Looks good to me. :)

Here's how I would write it

    main = print . process . lines =<< readFile "fruits.txt"

    process :: [String] -> [(String,Int)]
    process = Map.toAscList . foldl f Map.empty
        where f map x = Map.insertWith (+) x 1 map


So, the  parseFile  function is best expressed as a left fold and
function composition is good style.


The above works fine for smaller files. If you have larger amounts of
data, you will need a few strictness annotations or there will be a
space leak. In this case, this means using the functions

    foldl'  and  Map.insertWith'

instead of

    foldl   and  Map.insertWith

But I wouldn't bother about that for now.


Regards,
apfelmus

--
http://apfelmus.nfshost.com