11

I try to write a program which will count the frequency of each element in a list.

    In: "aabbcabb"
    Out: [("a",3),("b",4),("c",1)]

You can view my code in the following link: http://codepad.org/nyIECIT2 In this code the output of unique function would be like this

     In: "aabbcabb"
     Out: "abc"

Using the output of unique we wil count the frequency of the target list. You can see the code here also:

    frequencyOfElt xs=ans
       where ans=countElt(unique xs) xs
          unique []=[]
      unique xs=(head xs):(unique (filter((/=)(head xs))xs))
      countElt ref target=ans'
             where ans'=zip ref lengths
            lengths=map length $ zipWith($)(map[(=='a'),(==',b'),(==',c')](filter.(==))ref)(repeat target)

    Error:Syntax error in input (unexpected symbol "unique") 

But in ghci 6.13 other type of error are showing also

Few asked me what is the purpose of using [(=='a'),(==',b'),(==',c')]. What I expect: If ref="abc" and target="aabbaacc" then

    zipWith($) (map filter ref)(repeat target)

will show ["aaaa","bb","cc"] then I can use map length over this to get the frequency Here for filtering list according with the ref i use [(=='a'),(==',b'),(==',c')]

I assume some logical error lies [(=='a'),(==',b'),(==',c')] here..

Chris Stryczynski
  • 30,145
  • 48
  • 175
  • 286
sabu
  • 297
  • 1
  • 7
  • 23
  • 1
    put the code and error in your question. – Marcin Nov 22 '12 at 16:54
  • 1
    Your indentation is wrong. Bindings in the same `where` clause must start in the same column. – Daniel Fischer Nov 22 '12 at 17:11
  • @Daniel: indentation is wrong here only. u can see my code here http://codepad.org/3WdfZKev. I know i will get help from u – sabu Nov 22 '12 at 17:25
  • no it's wrong there too. You can see the line number colored in red for you to notice it better. It says there "ERROR line 3 - Syntax error in input (unexpected symbol "unique")". To not make such error, make the keyword "where" the only word on a line. – Will Ness Nov 22 '12 at 17:29
  • @WillNess: Now see the code, pls: http://codepad.org/i1gfAH4t – sabu Nov 22 '12 at 17:34
  • 2
    @SaugataBose Repeat after me: "*All* bindings in the same where clause must start in the same column." Then compare the columns of `ans` and `unique`, which are in the same where clause. – Ingo Nov 22 '12 at 17:34
  • @SaugataBose new error is also shown there. I copy: "ERROR line 7 - Improperly terminated character constant". Do not write such long lines. See here http://codepad.org/ZxhC7ZqW your code with shorter lines. You use there `',b'` but a character constant can hold only one character. – Will Ness Nov 22 '12 at 17:39
  • @SaugataBose Care to explain what you think this one: `map[(=='a'),(==',b'),(==',c')]` is supposed to mean? – Ingo Nov 22 '12 at 17:40
  • @Ingo: Thnx for ur effort. I try to find the list. If it is equal to a then it will produce element conating a, if its containing b it will produce element containng b. for ur considertion I put some test case in my question. – sabu Nov 22 '12 at 17:56
  • I'm going through your code step by step, check it out: http://hpaste.org/78070 . I'm not finished yet. :) – Will Ness Nov 22 '12 at 18:00
  • i m grateful for ur kind effrt – sabu Nov 22 '12 at 18:02
  • thanks, @Daniel ... I've just made some false steps there and ended up just rewriting it with simple list comprehension. But the OP's idea was actually nice - zipping with application!... It probably should be re-written again with Applicative ZipList or something... :) – Will Ness Nov 22 '12 at 18:29
  • And my hope increases as 2 mentors are engaged here.thnx for ur involvment – sabu Nov 22 '12 at 18:44
  • @WillNess: Will, did you find error there? – sabu Nov 22 '12 at 19:23
  • Your code wasn't right as is. For one thing, you can't map lists over lists; you can only map functions over lists. I've edited my answer. – Will Ness Nov 22 '12 at 19:35
  • Does this answer your question? [Haskell - Counting how many times each distinct element in a list occurs](https://stackoverflow.com/questions/10398698/haskell-counting-how-many-times-each-distinct-element-in-a-list-occurs) – Chris Stryczynski Jan 12 '21 at 23:53

2 Answers2

23

You didn't say whether you want to write it whole on your own, or whether it's OK to compose it from some standard functions.

import Data.List

g s = map (\x -> ([head x], length x)) . group . sort $ s

-- g = map (head &&& length) . group . sort     -- without the [...]

is the standard quick-n-dirty way to code it.


OK, so your original idea was to Code it Point-Free Style (certain tune playing in my head...):

frequencyOfElt :: (Eq a) => [a] -> [(a,Int)]
frequencyOfElt xs = countElt (unique xs) xs     -- change the result type
  where 
    unique [] = []
    unique (x:xs) = x : unique (filter (/= x) xs)  

    countElt ref target =   -- Code it Point-Free Style  (your original idea)
      zip 
        ref $               -- your original type would need (map (:[]) ref) here
        map length $
          zipWith ($)       -- ((filter . (==)) c) === (filter (== c))
            (zipWith ($) (repeat (filter . (==))) ref)  
            (repeat target)

I've changed the type here to the more reasonable [a] -> [(a,Int)] btw. Note, that

zipWith ($) fs (repeat z) === map ($ z) fs
zipWith ($) (repeat f) zs === map (f $) zs === map f zs

hence the code simplifies to

    countElt ref target =  
      zip 
        ref $              
        map length $
          map ($ target)      
            (zipWith ($) (repeat (filter . (==))) ref)  

and then

    countElt ref target =  
      zip 
        ref $              
        map length $
          map ($ target) $
            map (filter . (==)) ref

but map f $ map g xs === map (f.g) xs, so

    countElt ref target =  
      zip 
        ref $              
        map (length . ($ target) . filter . (==)) ref      -- (1)

which is a bit clearer (for my taste) written with a list comprehension,

    countElt ref target =  
        [ (c, (length . ($ target) . filter . (==)) c) | c <- ref] 
     == [ (c,  length ( ($ target) ( filter (== c))))  | c <- ref]     
     == [ (c,  length $ filter (== c) target)          | c <- ref]     

Which gives us an idea to re-write (1) further as

    countElt ref target =  
      zip <*> map (length . (`filter` target) . (==)) $ ref

but this obsession with point-free code becomes pointless here.


So going back to the readable list comprehensions, using a standard nub function which is equivalent to your unique, your idea becomes

import Data.List

frequencyOfElt xs = [ (c, length $ filter (== c) xs) | c <- nub xs]

This algorithm is actually quadratic (~ n^2), so it is worse than the first version above which is dominated by sort i.e. is linearithmic (~ n log(n)).


This code though can be manipulated further by a principle of equivalent transformations:

  = [ (c, length . filter (== c) $ sort xs) | c <- nub xs]

... because searching in a list is the same as searching in a list, sorted. Doing more work here -- will it pay off?..

  = [ (c, length . filter (== c) $ sort xs) | (c:_) <- group $ sort xs]

... right? But now, group had already grouped them by (==), so there's no need for the filter call to repeat the work already done by group:

  = [ (c, length . get c . group $ sort xs) | (c:_) <- group $ sort xs]
            where get c gs = fromJust . find ((== c).head) $ gs

  = [ (c, length g) | g@(c:_) <- group $ sort xs]

  = [ (head g, length g) | g <- group (sort xs)]

  = (map (head &&& length) . group . sort) xs

isn't it? And here it is, the same linearithmic algorithm from the beginning of this post, actually derived from your code by factoring out its hidden common computations, making them available for reuse and code simplification.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • 1
    Thanx. Thank u very much. But I want to keep Alive and try to klnw in which area i did ta mstake.thakkkk will – sabu Nov 22 '12 at 17:26
  • 2
    why `[head x]` instead of just `head x`? It is arbitrary to wrap the value in some construct without apparent reason. Hence confusing. – Ingo Nov 22 '12 at 17:29
  • @Ingo that's what the example answer in the Q is showing. Perhaps that was an oversight on the OP part. – Will Ness Nov 22 '12 at 17:33
  • @WillNess I see. But then, your solution can count not only characters. – Ingo Nov 22 '12 at 17:37
  • 1
    @Ingo but lists are homogeneous in Haskell, so all is good. :) – Will Ness Nov 22 '12 at 17:41
  • @WillNess Thanx for ur tremendous effort.Can i get the code in codapad link? – sabu Nov 22 '12 at 20:46
  • 1
    I will have that certain tune in my head all night now. >_ – MathematicalOrchid Nov 22 '12 at 22:36
  • 1
    @SaugataBose http://hpaste.org/78070 has most all the variants (including some false steps). Or copy from here; enter the editing history by clicking "edited X hours ago" below the post, and at the top revision, click ["source"](http://stackoverflow.com/revisions/549609fc-aed1-4cdc-8d3b-4b425f6dcbe2/view-source). – Will Ness Nov 23 '12 at 03:08
  • Another point-free way: `g = map (head &&& length) . group . sort` – 4castle Jun 08 '17 at 15:04
12

Using multiset-0.1:

import Data.Multiset

freq = toOccurList . fromList 
Landei
  • 54,104
  • 13
  • 100
  • 195