From: Kenneth Tilton on
Eric Wolf wrote:
> Hello there!
>
> I'm starting to learn lisp and doing the exercises of Ansi Common Lisp
> authored by Paul Graham.
>
> So my solution to counting occurences in a list and outputting a
> list of dotted pairs (symbol . count) sorted by count is
>
> (defun occurences-helper (lst result)
> (if (null lst)
> result
> (progn
> (let ((symb (car lst)))
> (let ((test (assoc symb result)))
> (if test
> (setf (cdr test) (+ (cdr test) 1))
> (setf result (cons (cons symb 1) result)))))
> (occurences-helper (cdr lst) result))))
>
> (defun occurences (lst)
> (sort
> (occurences-helper lst ())
> #'(lambda (x y)
> (>
> (cdr x)
> (cdr y))))
> )
>
>
> And I thought: "Hmm that doesn't look elegant" but I'm not used to
> recursion and functional programming and that stuff so I didn't
> came up with a better version.
>
> Maybe some experienced lisp hacker may provide a different one?

One issue here is "recursion obsession". It comes from Graham being more
a Schemer than a Lisper. Lispers rejoice in multiple paradigms, and try
to use the right one at the right time. In this case, when iterating
iterate. But if you want to do recursion:

(defun occurences (list)
(labels ((occ (list)
(fun-stuff (car list) (occ (cdr list))))
(occ list)))

ie, no need for an external helper, we have labels and flet.

Where is fun-stuff? You mostly have it already, just take what you have
and lose all the setfs and progns -- you /are/ reading Graham, aren't you?

Not a bad start, btw.

kt


From: Scott L. Burson on
On Jun 17, 3:48 pm, RG <rNOSPA...(a)flownet.com> wrote:
> In article <87vfr3Ft2...(a)mid.individual.net>,
>  Eric Wolf <e...(a)boese-wolf.eu> wrote:
>
>
>
> > Hello there!
>
> > I'm starting to learn lisp and doing the exercises of Ansi Common Lisp
> > authored by Paul Graham.
>
> > So my solution to counting occurences in a list and outputting a
> > list of dotted pairs (symbol . count) sorted by count is
>
> > (defun occurences-helper (lst result)
> >   (if (null lst)
> >       result
> >       (progn
> >         (let ((symb (car lst)))
> >           (let ((test (assoc symb result)))
> >             (if test
> >                 (setf (cdr test) (+ (cdr test) 1))
> >                 (setf result (cons (cons symb 1) result)))))
> >         (occurences-helper (cdr lst) result))))
>
> > (defun occurences (lst)
> >   (sort
> >    (occurences-helper lst ())
> >    #'(lambda (x y)
> >        (>
> >         (cdr x)
> >         (cdr y))))
> >   )
>
> > And I thought: "Hmm that doesn't look elegant" but I'm not used to
> > recursion and functional programming and that stuff so I didn't
> > came up with a better version.
>
> > Maybe some experienced lisp hacker may provide a different one?
>
> > Yours sincerely,
>
> > Eric
>
> IMHO a better way to approach this, particular for beginners, is to use
> abstract associative maps rather than association lists.  A CL
> implementation of abstract associative maps can be found here:
>
> http://www.flownet.com/ron/lisp/dictionary.lisp
>
> You'll also need the rg-utils.lisp file.
>
> Once you have abstract associative maps, you can make an occurrence
> counter trivially:
>
> (require :dictionary)
>
> (defclass counter (dictionary) ())
>
> (defmethod add ((c counter) item)
>   (setf (ref c item) (1+ (ref c item 0))))
>
> (defun count-items (list)
>   (let ((c (make-instance 'counter)))
>     (dolist (item list) (add c item))
>     c))
>
> ? (count-items '(one two two three three three))
> #<COUNTER HASH-TABLE { ONE 1 TWO 2 THREE 3 }>

With FSet it's even more trivial:

(defun count-items (list)
(convert 'bag list))

(Sorting is again left as an exercise.)

-- Scott
From: Nicolas Neuss on
"Frode V. Fjeld" <frode(a)netfonds.no> writes:

> (defun occurences-helper (list)
> (loop with result = nil
> for element in list
> do (incf (getf result element))
> finally (return result)))

Small correction: (getf result element 0)

Nicolas
From: Pascal Costanza on
On 18/06/2010 00:17, Pascal J. Bourguignon wrote:
>> (defun occurrences (list)
>> (loop with hash-table = (make-hash-table)
>> for item in list
>> do
>> (incf (gethash item hash-table 0))
>> finally
>> return (sort (loop for key being each hash-key of hash-table using (hash-value count)
>> collect (cons key count))
>> #'>
>> :key #'cdr)))
>>
>> Just as an antidote to Graham's style.
>
> But this is not ANSI CL, but CLtL1.
> FINALLY takes a compound-form+, not an unconditional.

By the way, what is the reason that you have to say "finally (return
....)" instead of just "finally return ..."? So in other words, why does
FINALLY not take an unconditional in ANSI CL? This is probably the most
common mistake I'm tripping over when using LOOP...


Pascal

--
My website: http://p-cos.net
Common Lisp Document Repository: http://cdr.eurolisp.org
Closer to MOP & ContextL: http://common-lisp.net/project/closer/
From: Pascal J. Bourguignon on

Pascal Costanza <pc(a)p-cos.net> writes:

> On 18/06/2010 00:17, Pascal J. Bourguignon wrote:
>>> (defun occurrences (list)
>>> (loop with hash-table = (make-hash-table)
>>> for item in list
>>> do
>>> (incf (gethash item hash-table 0))
>>> finally
>>> return (sort (loop for key being each hash-key of hash-table using (hash-value count)
>>> collect (cons key count))
>>> #'>
>>> :key #'cdr)))
>>>
>>> Just as an antidote to Graham's style.
>>
>> But this is not ANSI CL, but CLtL1.
>> FINALLY takes a compound-form+, not an unconditional.
>
> By the way, what is the reason that you have to say "finally (return
> ...)" instead of just "finally return ..."? So in other words, why
> does FINALLY not take an unconditional in ANSI CL? This is probably
> the most common mistake I'm tripping over when using LOOP...

I think it's explained by the + after compound-form: it allows for
several forms to be executed in sequence in the finally clause.

(loop for i below 10
collect i into result
finally (print 'done)
(assert (= 10 (length result)))
(return (remove-if 'oddp result)))

Perhaps also for symetry with the INITIALLY clause.


--
__Pascal Bourguignon__ http://www.informatimago.com/