From: Private Oracle
Subject: Please help
Date: 
Message-ID: <01be763a$7e1f16e0$LocalHost@ppnorn.atlant.ru>
Hi, folks! Your help badly needed!

I'm new to LISP and I've got a real bugger of
a problem here : I need to define a function 
which duplicates every occurance of a given 
element  in a given list. Suppose this funtion   
was called DUPLIC8, then  the call 
	
	(DUPLIC8 '(A B C D E B) 'B)) 

should produce (A B B C D E B B).

Cheers.

P.S. Must be easy as winking, this one for you. 
I could write this one in C with my eyes closed but
LISP is just not up my valley. No offence, though.


-- 
Lang may yer lum reek!
                               ~~~~~
  / \                        | | | |
/     \~~~~~~~~~~~~\

From: Stig Hemmer
Subject: Re: Please help
Date: 
Message-ID: <ekvu2v92yiq.fsf@bigblue.pvv.ntnu.no>
"Private Oracle" <····@atlant.ru> writes:
> 	(DUPLIC8 '(A B C D E B) 'B)
> should produce (A B B C D E B B).

(defun duplic8 (cons list) (mapcan #'(lambda (eq)(cons eq (if (eq eq
list)(list eq))))cons))

For extra credit, explain how this function works and why it is safe
to use MAPCAN here.

Stig Hemmer,
Jack of a Few Trades.
From: Fred Martin
Subject: Re: Please help
Date: 
Message-ID: <ucmK2.3900$L66.4085@news.rdc1.bc.wave.home.com>
Interesting homework assignment. Sounds like a job for "mapcar".
Private Oracle <····@atlant.ru> wrote in message
································@ppnorn.atlant.ru...
>
> Hi, folks! Your help badly needed!
>
> I'm new to LISP and I've got a real bugger of
> a problem here : I need to define a function
> which duplicates every occurance of a given
> element  in a given list. Suppose this funtion
> was called DUPLIC8, then  the call
>
> (DUPLIC8 '(A B C D E B) 'B))
>
> should produce (A B B C D E B B).
>
> Cheers.
>
> P.S. Must be easy as winking, this one for you.
> I could write this one in C with my eyes closed but
> LISP is just not up my valley. No offence, though.
>
>
> --
> Lang may yer lum reek!
>                                ~~~~~
>   / \                        | | | |
> /     \~~~~~~~~~~~~\
From: CHRISTOPHER ELIOT
Subject: Re: Please help
Date: 
Message-ID: <36fa46e5.0@rcfnews.cs.umass.edu>
In article <···················@news.rdc1.bc.wave.home.com>,
Fred Martin <··········@home.com> wrote:
>Interesting homework assignment. Sounds like a job for "mapcar".

I don't think "mapcar" can do it in one pass. I think you need
another function from the same family to do this functionally.

I believe there is also a simple recursive solution, and you can
do anything with LOOP if you understand it well enough.

>Private Oracle <····@atlant.ru> wrote in message
>································@ppnorn.atlant.ru...
>>
>> Hi, folks! Your help badly needed!
>>
>> I'm new to LISP and I've got a real bugger of
>> a problem here : I need to define a function
>> which duplicates every occurance of a given
>> element  in a given list. Suppose this funtion
>> was called DUPLIC8, then  the call
>>
>> (DUPLIC8 '(A B C D E B) 'B))
>>
>> should produce (A B B C D E B B).
>>
>> Cheers.
>>
>> P.S. Must be easy as winking, this one for you.
>> I could write this one in C with my eyes closed but
>> LISP is just not up my valley. No offence, though.
>>
>>
>> --
>> Lang may yer lum reek!
>>                                ~~~~~
>>   / \                        | | | |
>> /     \~~~~~~~~~~~~\
>
>


-- 
Christopher R. Eliot, Senior Postdoctoral Research Associate
Center for Knowledge Communication, Department of Computer Science
University of Massachusetts, Amherst 01003. (413) 545-4248 FAX: 545-1249
·····@cs.umass.edu,   <http://www.cs.umass.edu/~eliot/>
From: Private Oracle
Subject: What's MAPCAR for?
Date: 
Message-ID: <01be769a$3d33d200$LocalHost@ppnorn.atlant.ru>
Fred Martin <··········@home.com> wrote in article
<···················@news.rdc1.bc.wave.home.com>...
> Interesting homework assignment. Sounds like a job for "mapcar".
> Private Oracle <····@atlant.ru> wrote in message
> ································@ppnorn.atlant.ru...
> > I need to define a function
> > which duplicates every occurance of a given
> > element  in a given list. Suppose this funtion
> > was called DUPLIC8, then  the call
> > (DUPLIC8 '(A B C D E B) 'B))
> > should produce (A B B C D E B B).
> >
> 
I was wondering what MAPCAR does. Could anyone help
From: Sunil Mishra
Subject: Re: What's MAPCAR for?
Date: 
Message-ID: <efyn211v7j6.fsf@cleon.cc.gatech.edu>
[Follow-ups trimmed.]

"Private Oracle" <····@atlant.ru> writes:

> I was wondering what MAPCAR does. Could anyone help

MAPCAR is a mapping function that takes a function and a list as arguments, 
and produces a result list composed of the result of calling the function
on the items of the list.

Examples:

(mapcar #'1+ '(1 2 3)) => (2 3 4)
(mapcar #'+ '(1 2 3) '(4 5 6 7)) => (5 7 9)

A simple (incomplete) implementation is:

(defun imapcar (fn list)
  (if (null list)
      nil
      (cons (funcall fn (car list)) (imapcar fn (cdr list)))))

A more complete implementation that handles multiple lists (example 2) is:

(defun mapcar (fn first-list &rest rest-lists)
  (if (or (null first-list) (some #'null rest-lists))
      nil
      (cons (apply fn (car first-list) (mapcar #'car rest-lists))
            (apply #'mapcar fn (cdr first-list) (mapcar #'cdr rest-lists)))))

Not very pretty, but the second version simply generalizes the first to
work with multiple lists as input. Note that the function must be defined
appropriately to take the right number of arguments, or an error will
result.

Pick up your favorite lisp textbook to find out more.

Sunil
From: Marco Antoniotti
Subject: Re: Please help
Date: 
Message-ID: <lwoglh997m.fsf@copernico.parades.rm.cnr.it>
"Fred Martin" <··········@home.com> writes:

> Interesting homework assignment. Sounds like a job for "mapcar".

Nope! MAPCAR won't do it! :)


> Private Oracle <····@atlant.ru> wrote in message
> ································@ppnorn.atlant.ru...
> >
> > Hi, folks! Your help badly needed!
> >
> > I'm new to LISP and I've got a real bugger of
> > a problem here : I need to define a function
> > which duplicates every occurance of a given
> > element  in a given list. Suppose this funtion
> > was called DUPLIC8, then  the call
> >
> > (DUPLIC8 '(A B C D E B) 'B))
> >
> > should produce (A B B C D E B B).
> >
> > Cheers.
> >
> > P.S. Must be easy as winking, this one for you.
> > I could write this one in C with my eyes closed but
> > LISP is just not up my valley. No offence, though.
> >

Let's help the student!

(defun duplic8 (the-list element)
   (cond ((null the-list) '())
         ((eql element (first the-list))
          (append (list element element)
                  (duplic8 (rest the-list) element)))
         (t (cons (first the-list) (duplic8 (rest the-list) element)))))

(defun duplic8 (the-list element)
   (mapcan #'(lambda (x)                ;; You need MAPCAN
		(if (eql x element)
                   (list x x)
                   (list x)))
           the-list))


(defun duplic8 (the-list element)
   (loop for x in the-list
         if (eql x element)
           nconc (list x x)
         else
           nconc (list x)))

(defun duplic8 (the-list element)
   (loop for x in the-list
         if (eql x element)
           append (list x x)
         else
           append (list x)))

-- 
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - 06 68 10 03 17, fax. +39 - 06 68 80 79 26
http://www.parades.rm.cnr.it/~marcoxa
From: Raymond Wiker
Subject: Re: Please help
Date: 
Message-ID: <87iubpj1ra.fsf@foobar.orion.no>
Marco Antoniotti <·······@copernico.parades.rm.cnr.it> writes:

> "Fred Martin" <··········@home.com> writes:
> 
> > Interesting homework assignment. Sounds like a job for "mapcar".
> 
> Nope! MAPCAR won't do it! :)

        Hmmm?

(defun duplic8 (list item)
  (apply #'append
         (mapcar (lambda (x)
                   (if (eql x item)
                       (list x x)
                     (list x)))
                 list)))

        --- which does not mean that I think it's a particularly
elegant solution :-)  

> > Private Oracle <····@atlant.ru> wrote in message
> > ································@ppnorn.atlant.ru...
> > >
> > > Hi, folks! Your help badly needed!
> > >
> > > I'm new to LISP and I've got a real bugger of
> > > a problem here : I need to define a function
> > > which duplicates every occurance of a given
> > > element  in a given list. Suppose this funtion
> > > was called DUPLIC8, then  the call
> > >
> > > (DUPLIC8 '(A B C D E B) 'B))
> > >
> > > should produce (A B B C D E B B).
> > >
> > > Cheers.
> > >
> > > P.S. Must be easy as winking, this one for you.
> > > I could write this one in C with my eyes closed but
> > > LISP is just not up my valley. No offence, though.
> > >
> 
> Let's help the student!
> 
> (defun duplic8 (the-list element)
>    (cond ((null the-list) '())
>          ((eql element (first the-list))
>           (append (list element element)
>                   (duplic8 (rest the-list) element)))
>          (t (cons (first the-list) (duplic8 (rest the-list) element)))))
> 
> (defun duplic8 (the-list element)
>    (mapcan #'(lambda (x)                ;; You need MAPCAN
> 		(if (eql x element)
>                    (list x x)
>                    (list x)))
>            the-list))
> 
> 
> (defun duplic8 (the-list element)
>    (loop for x in the-list
>          if (eql x element)
>            nconc (list x x)
>          else
>            nconc (list x)))
> 
> (defun duplic8 (the-list element)
>    (loop for x in the-list
>          if (eql x element)
>            append (list x x)
>          else
>            append (list x)))
> 
> -- 
> Marco Antoniotti ===========================================
> PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
> tel. +39 - 06 68 10 03 17, fax. +39 - 06 68 80 79 26
> http://www.parades.rm.cnr.it/~marcoxa

-- 
Raymond Wiker, Orion Systems AS
+47 370 61150
From: ··········@scientia.com
Subject: Re: Please help
Date: 
Message-ID: <7ddg3k$dpu$1@nnrp1.dejanews.com>
In article <··············@foobar.orion.no>,
  Raymond Wiker <·······@orion.no> wrote:
> (defun duplic8 (list item)
>   (apply #'append
>          (mapcar (lambda (x)
>                    (if (eql x item)
>                        (list x x)
>                      (list x)))
>                  list)))
>
>         --- which does not mean that I think it's a particularly
> elegant solution :-)
>

One of the utilities I always have lying around is mapappend, which works
like mapcar, but has the effect of appending together all the result lists.
(there's a definition e.g. in AoMOP). With such a utiltiy defined you could
write:


(defun duplic8 (list item)
  (mapappend #'(lambda (x) (cons x (when (eql x item) (list x)))) list))


(Yuck! :-))


-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Gareth McCaughan
Subject: Re: Please help
Date: 
Message-ID: <86677pmt0b.fsf@g.pet.cam.ac.uk>
Paul Rudin wrote:

> One of the utilities I always have lying around is mapappend, which works
> like mapcar, but has the effect of appending together all the result lists.
> (there's a definition e.g. in AoMOP). With such a utiltiy defined you could
> write:
> 
> (defun duplic8 (list item)
>   (mapappend #'(lambda (x) (cons x (when (eql x item) (list x)))) list))

What's wrong with MAPCAN?

-- 
Gareth McCaughan       Dept. of Pure Mathematics & Mathematical Statistics,
·····@dpmms.cam.ac.uk  Cambridge University, England.
From: ·····@my-dejanews.com
Subject: Re: Please help
Date: 
Message-ID: <7dgrsv$dt3$1@nnrp1.dejanews.com>
In article <··············@g.pet.cam.ac.uk>,
  Gareth McCaughan <·····@dpmms.cam.ac.uk> wrote:
>
> What's wrong with MAPCAN?
>


Nothing, I guess, but I normally avoid writing anything with destructive
functions first time around.









-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Kent M Pitman
Subject: Re: Please help
Date: 
Message-ID: <sfwzp4zn6la.fsf@world.std.com>
·····@my-dejanews.com writes:

> In article <··············@g.pet.cam.ac.uk>,
>   Gareth McCaughan <·····@dpmms.cam.ac.uk> wrote:
> >
> > What's wrong with MAPCAN?
> 
> Nothing, I guess, but I normally avoid writing anything with destructive
> functions first time around.

Why?

This piece of religious nonsense needs to have a wooden stake driven
through its heart, IMO.  Apologies in advance for coming down hard on it,
but his place would be no fun without someone having a bit of zeal about
their opinions, and if people are going to say destructive is bad, they'd
better be prepared to hear the alternative point of view.

If I reword this to say exactly the same thing in a different tone,
is it really defensible?

  "I avoid writing anything using obvious and efficient tools
   first time around."

When you prepare dinner, do you start by cutting through all your fruits
and vegetables with imaginary, non-destructive implements hoping that will
be enough and come back to use a real knife only if you find out that
doesn't work?  Some engineering practice involves the use of tools which
are dangerous if used wrong but still the right thing to apply on the
very first task.

MAPCAN can have its problems and may not be the right thing to use for 
reasons of conceptual understanding.  But using MAPAPPEND (if there 
were such a thing) would not substantially fix many of the problems with
MAPCAN.  And certainly there are many cases where using NREVERSE (another
destructive operator) is absolutely the right thing to do.  The following
idiom should never be written first with REVERSE:

 (let ((l '()))
   (dolist (f frobs)
     (if (good-p f) (push f l)))
   (nreverse l))

Note also the use of PUSH.  You could claim that was destructive and to be
avoided, too, but to what possible end?

Style is one thing, dogma is quite another.
Saying "destructive is bad" seems to me indefensible.
Just my opinion.
Feel free to defend the alternative. ;-)
From: Vassil Nikolov
Subject: Re: Please help
Date: 
Message-ID: <7djnlt$nuo$1@nnrp1.dejanews.com>
Besides, `if people are going to say destructive is bad, they'd
better' write their own sorting routine and forget about CL:SORT.

(Quoted phrase from article <···············@world.std.com> by
Kent M Pitman <······@world.std.com>.)

Vassil Nikolov <········@poboxes.com> www.poboxes.com/vnikolov
(You may want to cc your posting to me if I _have_ to see it.)
   LEGEMANVALEMFVTVTVM  (Ancient Roman programmers' adage.)

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Mark K. Gardner
Subject: Re: Please help
Date: 
Message-ID: <slrn7fpoli.3gt.mkgardne@rtsl3.cs.uiuc.edu>
First, I must say that I enjoy Kent's insights very much. To "lock
horns" with him is both an honor and a fearful undertaking... :-)

On Sat, 27 Mar 1999 00:01:37 GMT, Kent M Pitman <······@world.std.com> wrote:
>·····@my-dejanews.com writes:
>
>> In article <··············@g.pet.cam.ac.uk>,
>>   Gareth McCaughan <·····@dpmms.cam.ac.uk> wrote:
>> >
>> > What's wrong with MAPCAN?
>> 
>> Nothing, I guess, but I normally avoid writing anything with destructive
>> functions first time around.
>
>Why?
>
>This piece of religious nonsense needs to have a wooden stake driven
>through its heart, IMO.  Apologies in advance for coming down hard on it,
>but his place would be no fun without someone having a bit of zeal about
>their opinions, and if people are going to say destructive is bad, they'd
>better be prepared to hear the alternative point of view.

[Thought provoking example deleted to conserve space.]

>Style is one thing, dogma is quite another.
>Saying "destructive is bad" seems to me indefensible.
>Just my opinion.
>Feel free to defend the alternative. ;-)

For me, attempting a purely functional solution first is an attempt to
re-adjust my thinking from the thorough procedural endoctrination I
have recieved. As I gain more maturity in Lisp, I suspect that I too
will develop the skills of *how* (and the instinct to know *when*) to
code a functional solution. Until then, I purposefully follow a
non-destructive dogma to gain experience. (I suspect at that point I
will reach the same conclusion as Kent.)

Mark

-- 
Mark K. Gardner (········@cs.uiuc.edu)
University of Illinois at Urbana-Champaign
Real-Time Systems Laboratory
-- 
From: Christopher C Stacy
Subject: Re: Please help
Date: 
Message-ID: <x8l4sn72v4d.fsf@world.std.com>
His dogma ran over his lambda.
From: Raymond Wiker
Subject: Re: Please help
Date: 
Message-ID: <87vhfn5hwa.fsf@foobar.orion.no>
Christopher C Stacy <······@world.std.com> writes:

> His dogma ran over his lambda.

        Shouldn't that be "His dogma ran away with his lambda" (makes
marginally more sense to me... :-)

-- 
Raymond Wiker, Orion Systems AS
+47 370 61150
From: ··········@scientia.com
Subject: Re: Please help
Date: 
Message-ID: <7ecg38$e26$1@nnrp1.dejanews.com>
In article <···············@world.std.com>,
  Kent M Pitman <······@world.std.com> wrote:
> ·····@my-dejanews.com writes:
>
> > In article <··············@g.pet.cam.ac.uk>,
> >   Gareth McCaughan <·····@dpmms.cam.ac.uk> wrote:
> > >
> > > What's wrong with MAPCAN?
> >
> > Nothing, I guess, but I normally avoid writing anything with destructive
> > functions first time around.
>
> Why?
>
> This piece of religious nonsense needs to have a wooden stake driven
> through its heart, IMO.  Apologies in advance for coming down hard on it,
> but his place would be no fun without someone having a bit of zeal about
> their opinions, and if people are going to say destructive is bad, they'd
> better be prepared to hear the alternative point of view.
>

It's got nothing to do with religion. But it does help in writing bug-free
code. Of course when things need to be done effeciently then it's worth
investing the effort, but there's an extra level of checking required when
bits of code aren't purely functional.


-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Tim Bradshaw
Subject: Re: Please help
Date: 
Message-ID: <nkj3e2dhb58.fsf@tfeb.org>
··········@scientia.com writes:


> It's got nothing to do with religion. But it does help in writing bug-free
> code. Of course when things need to be done effeciently then it's worth
> investing the effort, but there's an extra level of checking required when
> bits of code aren't purely functional.

Fortunately the extra level of checking is as nothing compared to the
years of mental gymnastics spent trying to represent the system you're
trying to model in a purely functional way.

--tim
From: ··········@scientia.com
Subject: Re: Please help
Date: 
Message-ID: <7ef02e$f6f$1@nnrp1.dejanews.com>
In article <···············@tfeb.org>,
  Tim Bradshaw <···@tfeb.org> wrote:
> ··········@scientia.com writes:
>
> > It's got nothing to do with religion. But it does help in writing bug-free
> > code. Of course when things need to be done effeciently then it's worth
> > investing the effort, but there's an extra level of checking required when
> > bits of code aren't purely functional.
>
> Fortunately the extra level of checking is as nothing compared to the
> years of mental gymnastics spent trying to represent the system you're
> trying to model in a purely functional way.

I don't agree with that (expect for IO). There are perfectly reasonable
purely functional programs, and indeed, languages in which you have to work
hard to do anything else.

To restate. In the normal course of things I use cons rather than push,
append rather than nconc etc. because this tends to produce more
maintainable, and bug- free code.

To be honest I'm a little confused that others are disagreeing with this.
Look through code that is posted here, or in books, or on ftp sites; and this
is the way most CL code is written. There are plenty of places where
destructive functions could replace their non-destructive counterparts, but 
people don't bother except where it really matters. The overhead in checking
whether side effects are desirable (or at least no harmful) usual isn't worth
it in order to save a little bit of garbage.





-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Erik Naggum
Subject: Re: Please help
Date: 
Message-ID: <3132490161714324@naggum.no>
* ··········@scientia.com
| In the normal course of things I use cons rather than push, append rather
| than nconc etc. because this tends to produce more maintainable, and bug-
| free code.
| 
| To be honest I'm a little confused that others are disagreeing with this.

  that's a clear sign there's enlightenment to be found in becoming
  unconfused...

  what people object to is the creation of garbage for no purpose, not the
  value of a more functional style.  just as with any other resource, there
  is no excuse for wanton waste, but that's what you favor and you think
  the textbooks favor, but the latter is not quite so.

  textbooks are allowed to introduce complexity in steps.  students are
  supposed to learn the full complexity of the issue through these steps,
  not stay at some step and not move on.  you argue as if students should
  _not_ move on, because the complexity of the "don't wantonly waste the
  resources" supposedly produces less maintainable and buggier code.  if I
  were to assume a psychological explanation for this mild delusion, it
  would be that you had been hurt by some mistake of this kind early on and
  have not quite learned to trust your skills afterwards.  now, please note
  that even if you don't like this explanation, you should not be surprised
  when more experienced programmers naturally gravitate towards that kind
  of explanation.  as you get more experience, the silly bugs created by
  using destructive operations in the wrong places should go away simply
  because that's that experience is all about.

  also, functional programming has enough problems being viewed as the
  right thing that it does not need to be associated with needless creation
  of garbage.  that's the kind of laziness no programmer should be proud of.

  if nothing else, I hope you understand why people "disagree" with your
  view that this is about functional style.

#:Erik
From: Tim Bradshaw
Subject: Re: Please help
Date: 
Message-ID: <nkjd81gtmvm.fsf@tfeb.org>
Erik Naggum <····@naggum.no> writes:

>   what people object to is the creation of garbage for no purpose, not the
>   value of a more functional style.  just as with any other resource, there
>   is no excuse for wanton waste, but that's what you favor and you think
>   the textbooks favor, but the latter is not quite so.

I don't object to that.  Well, I do, but less than I object to the
Functional-Programming-Is-The-One-True-Way mantra.  In particular the
f-p-i-t-o-t-w people will argue that if you are sufficiently smart you
can write compilers that avoid all that garbage creation by knowing
where you can actually be destructive behind the scenes, and also that
seriously ephemeral garbage is not so bad with modern GCs.  I don't
even object to functional programming I think, I object to the
One-True-Way bit.

--tim
From: ··········@scientia.com
Subject: Re: Please help
Date: 
Message-ID: <7eke1t$288$1@nnrp1.dejanews.com>
In article <················@naggum.no>,
  Erik Naggum <····@naggum.no> wrote:
> * ··········@scientia.com
> | In the normal course of things I use cons rather than push, append rather
> | than nconc etc. because this tends to produce more maintainable, and bug-
> | free code.
> |
> | To be honest I'm a little confused that others are disagreeing with this.
>
>   that's a clear sign there's enlightenment to be found in becoming
>   unconfused...
>
>   what people object to is the creation of garbage for no purpose, not the
>   value of a more functional style.  just as with any other resource, there
>   is no excuse for wanton waste, but that's what you favor and you think
>   the textbooks favor, but the latter is not quite so.

But of course it's not for "no purpose". The purpose is to write correct code
first time round as often as possible. If you *really* want to avoid waste
write assembler, or indeed machine code :-)

Note that I didn't suggest that programs should  be purely functional. I said
that in the normal course of things I use functional code rather that
destructive code first time round, all other things being equal. Then decide
where things can be modified for effeciency. Sometimes the code you writing
doesn't suit this style, because the data structures and algorithms you've
decided are naturally expressed in an imperative, destuctive fashion. (As it
happens I've spend most of this week writing code to shuffle bits around bit
vectors, because this is the only practical way to get the kind of performance
needed for the task in hand.)

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Erik Naggum
Subject: Re: Please help
Date: 
Message-ID: <3132644015217581@naggum.no>
* ··········@scientia.com
| But of course it's not for "no purpose".  The purpose is to write correct
| code first time round as often as possible.

  that is supposed to be the consequence of successful training or
  education.  if you have to do something silly to get correct code, that
  _should_ have been a very strong warning sign.

| If you *really* want to avoid waste write assembler, or indeed machine
| code :-)

  idiot.

| Then decide where things can be modified for effeciency.

  if you have no concept of what is or is not efficient before you start,
  doing it afterwards will be very, very inefficient, in programmer time.

  you appear from the above comment not to understand that "wanton waste"
  is not restricted to what the computer does.  I suggest you broaden your
  perspective and appreciate that the whole idea of programming computers
  is to reduce the total cost of the tasks the computer is set to do.

| Sometimes the code you writing doesn't suit this style, because the data
| structures and algorithms you've decided are naturally expressed in an
| imperative, destuctive fashion. (As it happens I've spend most of this
| week writing code to shuffle bits around bit vectors, because this is the
| only practical way to get the kind of performance needed for the task in
| hand.)

  at least this looks like a redeeming quality, but I'm uncertain about how
  you decide on data structures and algorithms to begin with.

  in my view, functional programming is not usefully extended all the way
  down (i.e., we don't want non-destructive instructions or memory, which
  would have meant we'd need a new computer every billionth of a second),
  so clearly there's a point where the exported interface is functional and
  the implementation is not.  I extend this view to functions at any level:
  functional programming is about a design style that _exports_ a clean
  interface, but I should _not_ care what it does inside.  consequently, if
  I design a function with a clean interface, it matters not whether I use
  a "dirty" technique or not inside it as long as nobody is impacted by it.
  this, incidentally, leads to why I favor a more functional programming
  style: I don't have to deal with any "ownership protocol" of objects that
  have been allocated, but when I do know that I alone own an object, I
  have no compulsions about modifying it, or asking functions I call to
  modify it.  and I _don't_ see this as betraying the Functional Style.

#:Erik
From: Tim Bradshaw
Subject: Re: Please help
Date: 
Message-ID: <nkj7lroa901.fsf@tfeb.org>
··········@scientia.com writes:

> I don't agree with that (expect for IO). There are perfectly reasonable
> purely functional programs, and indeed, languages in which you have to work
> hard to do anything else.
> 

But is there a perfecly reasonable purely functional *world* out
there?  That tends to be a more interesting question for many people
trying to write programs which talk about stuff outside the machine.

--tim
From: ··········@scientia.com
Subject: Re: Please help
Date: 
Message-ID: <7ekgl3$438$1@nnrp1.dejanews.com>
In article <···············@tfeb.org>,
  Tim Bradshaw <···@tfeb.org> wrote:
> ··········@scientia.com writes:
>
> > I don't agree with that (expect for IO). There are perfectly reasonable
> > purely functional programs, and indeed, languages in which you have to work
> > hard to do anything else.
> >
>
> But is there a perfecly reasonable purely functional *world* out
> there?  That tends to be a more interesting question for many people
> trying to write programs which talk about stuff outside the machine.

Being "functional" is not a property of the world, it's a property of
mathematical abstractions of it. And for any reasonably rigourous "non-
functional" model there's an essentially equivalent functional model (subject
to suitable definitions).

This is getting a little away from the oringinal point, but since we're
there: what I'd quite like is a good higher-order relational language to
program in. With functional languages you get funtions and higher-order
quantification, with prolog and the like you get relations, but only first
order quantification.



-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Tim Bradshaw
Subject: Re: Please help
Date: 
Message-ID: <nkjzp4i3vxt.fsf@tfeb.org>
··········@scientia.com writes:

> 
> Being "functional" is not a property of the world, it's a property of
> mathematical abstractions of it. And for any reasonably rigourous "non-
> functional" model there's an essentially equivalent functional model (subject
> to suitable definitions).

Yes, of course, but that model may be much harder to use, and much
further from people's intuitions.  Sufficiently hard and far that for
most purposes it's useless.

--tim
From: ··········@scientia.com
Subject: Re: Please help
Date: 
Message-ID: <7ekvm9$ftd$1@nnrp1.dejanews.com>
In article <···············@tfeb.org>,
  Tim Bradshaw <···@tfeb.org> wrote:
> ··········@scientia.com writes:
>
> >
> > Being "functional" is not a property of the world, it's a property of
> > mathematical abstractions of it. And for any reasonably rigourous "non-
> > functional" model there's an essentially equivalent functional model
(subject
> > to suitable definitions).
>
> Yes, of course, but that model may be much harder to use, and much
> further from people's intuitions.  Sufficiently hard and far that for
> most purposes it's useless.


Well, this depends very much on poeple's intuitions work; which is partly
dependent on the way they've been taught. If you teach people functional
programming from the start then they'll tend to have intuitions that fit in
with this. If they've been brought up on Pascal, Basic and C then they're
likley to initially struggle with functional models of problem areas.


I think it's a lot like learning category theory after having had a fairly
traditional mathematical education: very little fits well with your
intuitions; but in the long run it's worth the effort because you get a new
perspective on your existing knowledge.



-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Tim Bradshaw
Subject: Re: Please help
Date: 
Message-ID: <nkjogkxlssc.fsf@tfeb.org>
··········@scientia.com writes:

> Well, this depends very much on poeple's intuitions work; which is partly
> dependent on the way they've been taught. If you teach people functional
> programming from the start then they'll tend to have intuitions that fit in
> with this. If they've been brought up on Pascal, Basic and C then they're
> likley to initially struggle with functional models of problem areas.
> 

Unfortunately most people develop a world model quite a long time
before they learn to program!

--tim
From: ··········@scientia.com
Subject: Re: Please help
Date: 
Message-ID: <7es8r6$54i$1@nnrp1.dejanews.com>
In article <···············@tfeb.org>,
  Tim Bradshaw <···@tfeb.org> wrote:
> ··········@scientia.com writes:
>
> > Well, this depends very much on poeple's intuitions work; which is partly
> > dependent on the way they've been taught. If you teach people functional
> > programming from the start then they'll tend to have intuitions that fit in
> > with this. If they've been brought up on Pascal, Basic and C then they're
> > likley to initially struggle with functional models of problem areas.
> >
>
> Unfortunately most people develop a world model quite a long time
> before they learn to program!

I should probably let this drop now, but I'll have one more go.

I'd guess that people tend to have small models of various parts of the world,
and at the points of overlap they may often be inconsistent. Any particular
program or part thereof that attempt to model some really small part of  the
real world may be built using intuitions that don't need to be  to cope able
(and in practice can't anway) with larger things.


IMO it helps to expose yourself to lots of different kinds of techniques for
writing different code. You learn new and productive ways of building
intuitions about different problem areas that are in part forced on you by the
limitations of the techniques involved.


Writing (for example) purely functional code is hard if you haven't had any
pratcice at it, but then so is e.g. writing in prolog if you have no experiece
of that.


-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Erik Naggum
Subject: Re: Please help
Date: 
Message-ID: <3132918681535806@naggum.no>
* ··········@scientia.com
| IMO it helps to expose yourself to lots of different kinds of techniques
| for writing different code. You learn new and productive ways of building
| intuitions about different problem areas that are in part forced on you
| by the limitations of the techniques involved.

  pardon the sarcasm, but if you value learning so much, how come you
  haven't learned to write code that doesn't waste resources, yet, and
  instead want others to "learn" your wasteful ways?

#:Erik, still not opposed to functional style, but opposed to wanton waste
From: Tim Bradshaw
Subject: Re: Please help
Date: 
Message-ID: <ey3k8vhxzsh.fsf@lostwithiel.tfeb.org>
* paul rudin wrote:

> I'd guess that people tend to have small models of various parts of
> the world, and at the points of overlap they may often be
> inconsistent. Any particular program or part thereof that attempt to
> model some really small part of the real world may be built using
> intuitions that don't need to be to cope able (and in practice can't
> anway) with larger things.


I think this is my last point about this too.

I really think it's pretty important not to ignore the models people
have.  They may be for `small' domains, but they typically work pretty
well.  If I was writing software to, say, help mechanical engineers
design bridges, and I came up to these people and told them `hey, your
model of how bridges work is all crap, you should do this other thing
that I've invented', then they wouldn't hire me.  And they'd be
*right* not to hire me: their models work pretty well and build some
pretty good bridges, and have seen almost 100yrs of development and
testing, which is more than you can say for any software `engineering'
technique, be it FP or OOP or whatever. The track record of people out
in the real world is kind of impressive compared to software people,
actually, they do this impressive stuff like sending people to the
moon while we grovel in the dirt trying to get our OS's to stay up for
a day or so.

I'm not against FP in some domains (some theories of physics would
suit it to a tee), or to a generally functional leaning (which I have,
I'm a Lisp person after all), I just think that in many case software
people should be listening a lot more closely to the real engineers
who make real things with such brilliant success.

--tim
From: Lieven Marchand
Subject: Re: Please help
Date: 
Message-ID: <m3d81ha5l4.fsf@localhost.localdomain>
··········@scientia.com writes:

> It's got nothing to do with religion. But it does help in writing bug-free
> code. Of course when things need to be done effeciently then it's worth
> investing the effort, but there's an extra level of checking required when
> bits of code aren't purely functional.
> 

In many idiomatic uses of destructive operations, there isn't any
effort to be invested. For instance,

(let ((acc nil))
   (dolist (.....)
      ......
      (push item acc))
   (nreverse acc))

is an idiomatic way to collect some stuff together. NREVERSE is
destructive but the function this is part of is still purely
functional since it only destructively modifies things it has itself
created.

Likewise structs, arrays, vectors and objects can be reasonably
modified destructively in many cases.

The non-religious view of destructive operations is that they can
cause problems since they constrain the possible arguments of things
to be non-shared and so they should be used where appropriate. Just
like the good old GOTO and most of the others stuff some dogma's tell
you to never do or always do.


-- 
Lieven Marchand <···@bewoner.dma.be>
If there are aliens, they play Go. -- Lasker
From: Christopher J. Vogt
Subject: Re: Please help
Date: 
Message-ID: <370BB3F2.A36081F9@computer.org>
Lieven Marchand wrote:
> 
> ··········@scientia.com writes:
> 
> > It's got nothing to do with religion. But it does help in writing bug-free
> > code. Of course when things need to be done effeciently then it's worth
> > investing the effort, but there's an extra level of checking required when
> > bits of code aren't purely functional.
> >
> 
> In many idiomatic uses of destructive operations, there isn't any
> effort to be invested. For instance,
> 
> (let ((acc nil))
>    (dolist (.....)
>       ......
>       (push item acc))
>    (nreverse acc))

Gack!  I hope nobody is really using such an idiom, push/reverse is
unnecessary, and awfully inefficient.  I'm a loop user not a do user so I can't
give the code for doing this using do, but using loop:
(loop for item from 1 to 10
      collect item)
which is no doubt desctructive, keeping a pointer to the end of 
the list and using rplacd to add to the end of the list.

> 
> is an idiomatic way to collect some stuff together. NREVERSE is
> destructive but the function this is part of is still purely
> functional since it only destructively modifies things it has itself
> created.
> 
> Likewise structs, arrays, vectors and objects can be reasonably
> modified destructively in many cases.
> 
> The non-religious view of destructive operations is that they can
> cause problems since they constrain the possible arguments of things
> to be non-shared and so they should be used where appropriate. Just
> like the good old GOTO and most of the others stuff some dogma's tell
> you to never do or always do.
> 
> --
> Lieven Marchand <···@bewoner.dma.be>
> If there are aliens, they play Go. -- Lasker
From: Barry Margolin
Subject: Re: Please help
Date: 
Message-ID: <54QO2.378$kM2.45826@burlma1-snr2>
In article <·················@computer.org>,
Christopher J. Vogt <····@computer.org> wrote:
>Lieven Marchand wrote:
>> In many idiomatic uses of destructive operations, there isn't any
>> effort to be invested. For instance,
>> 
>> (let ((acc nil))
>>    (dolist (.....)
>>       ......
>>       (push item acc))
>>    (nreverse acc))
>
>Gack!  I hope nobody is really using such an idiom, push/reverse is
>unnecessary, and awfully inefficient.

Sorry, but that idiom is extremely well entrenched.  If you're not into
LOOP or Iterators/Collectors, and MAPCAR isn't convenient for the
particular use, that idiom is most common.  I've seen and written it myself
many times, although I eventually learned LOOP.

Unless the resulting list is large, the inefficiency of NREVERSE probably
isn't an issue.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Erik Naggum
Subject: Re: Please help
Date: 
Message-ID: <3132519122202573@naggum.no>
* Lieven Marchand
| (let ((acc nil))
|    (dolist (.....)
|       ......
|       (push item acc))
|    (nreverse acc))

* "Christopher J. Vogt" <····@computer.org>
| Gack!  I hope nobody is really using such an idiom, push/reverse is
| unnecessary, and awfully inefficient.

  how did "actually faster" get contorted into "awfully inefficient"?  you
  have never tried to profile code like this, have you?

| I'm a loop user not a do user so I can't give the code for doing this
| using do, but using loop:
| (loop for item from 1 to 10
|       collect item)
| which is no doubt desctructive, keeping a pointer to the end of the list
| and using rplacd to add to the end of the list.

  sigh.  you just made it harder for me to defend using LOOP.  but at least
  I know how to do it, which is why I prefer that LOOP do it for me...

  SETF of CDR (or RPLACD if you insist) is a destructive operation, and
  this has GC implications.  PUSH does not have GC implications.  the idiom
  is (setf foo (setf (cdr foo) (cons bar nil))), and as you should be able
  to see, this involves more work than (setf foo (cons bar foo)).

  also note that it is unspecified whether NREVERSE does its work by CARs
  or by CDRs.  both have interesting performance characteristics.

  note, however, that if you can convince the compiler to cons on the stack
  (a.k.a., dynamic-extent), and not on the heap, REVERSE at the end might
  be more efficient, since it can take advantage of the stack organization.
  this, however, would require compiler recognition of this particular
  idiom, and probably a waste of time to a vendor.  so expect free software
  to sport such an optimization opportunity.  ;)

#:Erik
From: Tim Bradshaw
Subject: Re: Please help
Date: 
Message-ID: <nkju2ur2yxd.fsf@tfeb.org>
"Christopher J. Vogt" <····@computer.org> writes:

> > 
> > (let ((acc nil))
> >    (dolist (.....)
> >       ......
> >       (push item acc))
> >    (nreverse acc))
> 
> Gack!  I hope nobody is really using such an idiom, push/reverse is
> unnecessary, and awfully inefficient.  I'm a loop user not a do user so I can't
> give the code for doing this using do, but using loop:
> (loop for item from 1 to 10
>       collect item)
> which is no doubt desctructive, keeping a pointer to the end of 
> the list and using rplacd to add to the end of the list.

This is fine, if you have a loop.  If you have some stuff which may
sometimes need to collect stuff, but isn't a loop, then PUSH/NREVERSE
is just fine.  I not only use the idiom, I teach it.  Believe me, you
don't want to teach people just learning lisp either how to keep tail
pointers by hand (might as well teach them C), or how to write a
COLLECTING macro (way too early to start dealing with
macro-defining-macros!), or that there is this magic COLLECTING thing
which solves the problem (destroys their model of how lists work, and
anyway is not in CL so they get confused about what is in the
language).

And although I have a COLLECTING macro that I use sometimes, I still
use PUSH/NREVERSE quite often, partly becaue my macro will only
collect one list at once and I often want to be splitting stuff into a
bunch of lists (OK, I could fix that, but the 8 line macro rapidly
becomes 100), but mostly because it's just a small constant factor at
best, and, actually, quite a lot of time stuff just isn't on the
critical path.

--tim
From: Don Geddis
Subject: Re: Please help
Date: 
Message-ID: <slrn7gq76o.iej.Webmaster@meta.Tesserae.COM>
In article <···············@tfeb.org>, Tim Bradshaw wrote:
> This is fine, if you have a loop.  [...]  there is this magic COLLECTING
> thing which solves the problem (destroys their model of how lists work, and
> anyway is not in CL so they get confused about what is in the language).

But LOOP is in Common Lisp, so why don't you use/teach that?

> And although I have a COLLECTING macro that I use sometimes, I still use
> PUSH/NREVERSE quite often, partly becaue my macro will only collect one list
> at once and I often want to be splitting stuff into a bunch of lists

Sounds like you should be using CL's LOOP :-).

	-- Don
-- 
Don Geddis     ······@cadabra.com     (650) 508-7893     Fax (650) 508-7891
Cadabra Inc.       275 Shoreline Drive, Suite 505, Redwood Shores, CA 94065
Comparison Shopping: Smarter, Better, Faster             http://cadabra.com
To be or not to be; that requires one TTL gate at a minimum, but you could do
it with three NAND gates, or just hook the output to Vcc.
From: Kelly Murray
Subject: Collecting/LOOP (was Re: Please help)
Date: 
Message-ID: <370D465E.6F9536E3@IntelliMarket.Com>
I think we've gone through this before...

The COLLECT feature of LOOP is nice until you want
something more complicated, like conditional collection,
which then introduces loop conditionals, and all language 
hell breaks loose.

I use push/nreverse all over, but a better construct would
be welcomed.  I haven't seen anything compelling to replace it.

One could just rename push/nreverse to collect/collected:

(macro collect (val var) `(setf {var} (cons {val} {var}))) 
(macro collected (var) `(setf {var} (nreverse {var}))) 

(loop with primes = nil 
      for i from 0 below 100
      finally (return (collected primes))
   do
   (if (primep i)
     then (collect i primes))
     )

Which seems OK, but doesn't seem to really add that much clarity,
though I would much prefer it over using COLLECT in LOOP itself.

-Kelly Murray  ···@niclos.com


Don Geddis wrote:
> 
> In article <···············@tfeb.org>, Tim Bradshaw wrote:
> > This is fine, if you have a loop.  [...]  there is this magic COLLECTING
> > thing which solves the problem (destroys their model of how lists work, and
> > anyway is not in CL so they get confused about what is in the language).
> 
> But LOOP is in Common Lisp, so why don't you use/teach that?
> 
> > And although I have a COLLECTING macro that I use sometimes, I still use
> > PUSH/NREVERSE quite often, partly becaue my macro will only collect one list
> > at once and I often want to be splitting stuff into a bunch of lists
> 
> Sounds like you should be using CL's LOOP :-).
> 
>         -- Don
> --
> Don Geddis     ······@cadabra.com     (650) 508-7893     Fax (650) 508-7891
> Cadabra Inc.       275 Shoreline Drive, Suite 505, Redwood Shores, CA 94065
> Comparison Shopping: Smarter, Better, Faster             http://cadabra.com
> To be or not to be; that requires one TTL gate at a minimum, but you could do
> it with three NAND gates, or just hook the output to Vcc.
From: Mark Stickel
Subject: Re: Collecting/LOOP (was Re: Please help)
Date: 
Message-ID: <wnzp4iwfwx.fsf@Pacific.AI.SRI.COM>
I also have reservations about the loop syntax
and want a convenient way of collecting values
other than loop or the push/nreverse idiom.

For example, what I would like to say is
 (let ((primes nil))
   (dotimes (i 100)
     (when (primep i)
       (collect i primes)))
   primes)
where collect is a constant-time operation that
collects values in the proper order.

You can write a collect macro with constant-time
proper order behavior that uses two places to
store results--the collected list and its tail.
The name of the second place can be computed at macro
expansion time from the first.

In the example above, collect would use
primes to store the list and primes-last to
store its tail.  Now the biggest flaw in the
approach is that the user must declare the second
place.  The code is exactly as above,
except for the added declaration of primes-last
 (let ((primes nil) primes-last)
   (dotimes (i 100)
     (when (primep i)
       (collect i primes)))
   primes)
Not perfect, but close.
From: Chris Riesbeck
Subject: Re: Collecting/LOOP (was Re: Please help)
Date: 
Message-ID: <riesbeck-0904991328100001@riesbeck.ils.nwu.edu>
In article <··············@Pacific.AI.SRI.COM>, ·······@Pacific.ai.sri.com
(Mark Stickel) wrote:

>... Now the biggest flaw in the
>approach is that the user must declare the second
>place.  The code is exactly as above,
>except for the added declaration of primes-last
> (let ((primes nil) primes-last)
>   (dotimes (i 100)
>     (when (primep i)
>       (collect i primes)))
>   primes)
>Not perfect, but close.

Something easy that I've implemented several times is

  (with-collector collect-prime
    (dotimes (i 100)
      (when (primep i)
        (collect-prime i))))

The return value of with-collector is the collected
list, same as with-output-to-string. 

Still, I've never seen anyone have trouble maintaining

   (loop for i from 1 to 100
         when (primep i)
         collect i)

*and* I bet more novices really meant 1 to 100 when
writing the latter and didn't mean 0 to 99 in the former.
From: Johan Kullstam
Subject: Re: Collecting/LOOP (was Re: Please help)
Date: 
Message-ID: <u1zhtod9w.fsf@res.raytheon.com>
·······@Pacific.ai.sri.com (Mark Stickel) writes:

> I also have reservations about the loop syntax
> and want a convenient way of collecting values
> other than loop or the push/nreverse idiom.
> 
> For example, what I would like to say is
>  (let ((primes nil))
>    (dotimes (i 100)
>      (when (primep i)
>        (collect i primes)))
>    primes)
> where collect is a constant-time operation that
> collects values in the proper order.

what's so wrong with the standard idiom?

  (let ((primes nil))
    (dotimes (i 100)
      (when (primep i)
        (push i primes)))
    (nreverse primes))

it *looks* just fine to me.  there's no accounting for taste, however,
and perhaps it doesn't appeal to your sense of aesthetics.  be that as
it may, the only complaint i can see is the efficiency aspect.  this
side of predlaring an array, push is about as cheap as you're ever
going to get.  is nreverse *that* expensive?

sometimes you do not need the nreverse, then you can just push.  this
happens when

1) you just don't care about the order of the result.
2) you can have the loop run backwards (in the primes
   collection case, count backwards from 100 downto 1).
3) doing two `push without reverse fix-up' operations in a row
   restores the original order.

i guess all this is obvious.  i just don't understand the dislike of
push/nreverse in the first place.

-- 
johan kullstam
From: Tim Bradshaw
Subject: Re: Collecting/LOOP (was Re: Please help)
Date: 
Message-ID: <ey3pv5da6tp.fsf@lostwithiel.tfeb.org>
* Johan Kullstam wrote:
> i guess all this is obvious.  i just don't understand the dislike of
> push/nreverse in the first place.

Well, judging by the C/C++ example, Lisp will never take over the
world if people don't spend vast amounts of time micro-optimising
their programs.  So this is definitely to be encouraged!

--tim
From: Raymond Toy
Subject: Re: Collecting/LOOP (was Re: Please help)
Date: 
Message-ID: <4nlng2uf2w.fsf@rtp.ericsson.se>
>>>>> "Kelly" == Kelly Murray <···@IntelliMarket.Com> writes:

    Kelly> I think we've gone through this before...
    Kelly> The COLLECT feature of LOOP is nice until you want
    Kelly> something more complicated, like conditional collection,
    Kelly> which then introduces loop conditionals, and all language 
    Kelly> hell breaks loose.

CMUCL has a COLLECT macro:

Macro documentation:
  Collect ({(Name [Initial-Value] [Function])}*) {Form}*
  Collect some values somehow.  Each of the collections specifies a bunch of
  things which collected during the evaluation of the body of the form.  The
  name of the collection is used to define a local macro, a la MACROLET.
  Within the body, this macro will evaluate each of its arguments and collect
  the result, returning the current value after the collection is done.  The
  body is evaluated as a PROGN; to get the final values when you are done, just
  call the collection macro with no arguments.

  Initial-Value is the value that the collection starts out with, which
  defaults to NIL.  Function is the function which does the collection.  It is
  a function which will accept two arguments: the value to be collected and the
  current collection.  The result of the function is made the new value for the
  collection.  As a totally magical special-case, the Function may be Collect,
  which tells us to build a list in forward order; this is the default.  If an
  Initial-Value is supplied for Collect, the stuff will be rplacd'd onto the
  end.  Note that Function may be anything that can appear in the functional
  position, including macros and lambdas.

Perhaps this is closer to what people are looking for?

Ray
From: Joerg-Cyril Hoehle
Subject: Re: Collecting/LOOP (was Re: Please help)
Date: 
Message-ID: <qkpwvzlom0q.fsf@tzd.dont.telekom.spam.de.me>
Kelly Murray <···@IntelliMarket.Com> writes:

> The COLLECT feature of LOOP is nice until you want
> something more complicated, like conditional collection,
> which then introduces loop conditionals, and all language 
> hell breaks loose.

> I use push/nreverse all over, but a better construct would
> be welcomed.  I haven't seen anything compelling to replace it.

You haven't followed comp.lang.lisp closely enough :-)

Tim Bradshaw posted the following:
From: Tim Bradshaw <···@aiai.ed.ac.uk>
Subject: Re: Append object to list - Question
Date: 05 Oct 1998 12:25:37 +0100
Message-ID: <···············@wiay.aiai.ed.ac.uk>

I tend to use this thing, that I wrote, but stole I'm sure from
somewhere else (possibly Interlisp?).  It probably needs to be
generalised somehow, not to mention cleaned up (all those dollar
signs...), but it's quite useful, especially if you want to collect
things over several loops or something.

    (defmacro collecting (&body forms)
      ;; Collect some random stuff into a list by keeping a tail-pointer
      ;; to it, return the collected list.  No real point in using
      ;; gensyms, although one probably should on principle.
      "Collect things into a list forwards.  Within the body of this macro
       The form `(COLLECT THING)' will collect THING into the list returned by 
       COLLECTING.  Uses a tail pointer -> efficient."
      (let (($resnam$ (gensym)) ($tail$ (gensym)) ($thing$ (gensym)))
	`(let
	   (,$resnam$ ,$tail$)
	   (macrolet
	     ((collect
		 (thing)
		;; Collect returns the thing it's collecting
		`(let ((,',$thing$ ,thing))
		   (if ,',$resnam$
		       (setf (cdr ,',$tail$)
			       (setf ,',$tail$ (list ,',$thing$)))
		       (setf ,',$resnam$
			     (setf ,',$tail$ (list ,',$thing$))))
		   ,',$thing$)))
	     ,@forms)
	   ,$resnam$)))

--tim

Also, Bruno Haible <······@ilog.fr> posted an almost identical
COLLECTING macro to the same effect some month ago.  Deja News yields:

Subject: Re: Simplified LOOP
Author: Bruno Haible <······@ilog.fr>
Date: 1998/02/06
Message-ID: <············@nz12.rz.uni-karlsruhe.de>

(defmacro collecting (&body forms)
  (let ((a (gensym))
        (b (gensym))
        (c (gensym)))
    `(LET ((,a NIL) (,b NIL))
       (MACROLET ((COLLECT (FORM)
                    `((LAMBDA (,',c)
                        (IF ,',a
                          (SETF (CDR ,',b) (SETF ,',b ,',c))
                          (SETF ,',a (SETF ,',b ,',c))
                      ) )
                      (LIST ,FORM)
                     )
                 ))
         (PROGN ,@forms)
         ,a
     ) )
) )
(defmacro collect (form)
  (error "collect used outside of collecting")
)

Also, while peeking at the CMUCL sources, I found in their code/PCL/
subdirectory a very interesting collection of iterator/collecting
macros which you could use as well.

The advantage of all these COLLECT forms is that thay can appear
lexically anywhere deep inside the collecting form -- unlike LOOP
clauses as you noticed.

Regards,
	Jorg Hohle
Telekom Research Center -- SW-Reliability
From: Marc Battyani
Subject: Iterate macros (Re: Collecting/LOOP...)
Date: 
Message-ID: <F72E408BB47C25ED.432DCDBEBEA0D885.21AB159746893979@library-proxy.airnews.net>
Joerg-Cyril Hoehle <············@tzd.dont.telekom.spam.de.me> wrote in
message ····················@tzd.dont.telekom.spam.de.me...
.....
> Also, while peeking at the CMUCL sources, I found in their code/PCL/
> subdirectory a very interesting collection of iterator/collecting
> macros which you could use as well.
>
> The advantage of all these COLLECT forms is that thay can appear
> lexically anywhere deep inside the collecting form -- unlike LOOP
> clauses as you noticed.
>
> Regards,
> Jorg Hohle
> Telekom Research Center -- SW-Reliability

I looked at the CMUCL PCL/iterate macro and found it quite interesting.
So I looked on the net and found the Jonathan Amsterdam iterate macro
which look even more interesting.

I have some questions about it:
    Has anybody used it?
    Is the 1.2 version the latest?
    It looks like it's more powerful than loop. Any comments on this?
    Efficiency?
    Are there other macro packages like this you know of?

Thanks,

Marc Battyani
From: Dobes Vandermeer
Subject: Re: Collecting/LOOP (Code Included)
Date: 
Message-ID: <370DA843.CD9863C0@mindless.com>
Kelly Murray wrote:
> 
> I think we've gone through this before...
> 
> The COLLECT feature of LOOP is nice until you want
> something more complicated, like conditional collection,
> which then introduces loop conditionals, and all language
> hell breaks loose.
> 
> I use push/nreverse all over, but a better construct would
> be welcomed.  I haven't seen anything compelling to replace it.
> 
> One could just rename push/nreverse to collect/collected:
> 
> (macro collect (val var) `(setf {var} (cons {val} {var})))
> (macro collected (var) `(setf {var} (nreverse {var})))
> 
> (loop with primes = nil
>       for i from 0 below 100
>       finally (return (collected primes))
>    do
>    (if (primep i)
>      then (collect i primes))
>      )
> 
> Which seems OK, but doesn't seem to really add that much clarity,
> though I would much prefer it over using COLLECT in LOOP itself.

A "collector" structure is be more useful for this, and very easy to
write (yeah, sometimes we cant use CL provided functions for EVERYTHING)

(defstruct collector 
  "Collect a list of objects easily and flexibly."
  head
  tail
  )

(defmethod collect ((c collector) obj)
   "Add a new object to the collector."
   
   ; Check if this is the first object
   (if (collector-tail c)
      ; Add it to the entry after the tail
      (progn
        (setf (cdr (collector-tail c)) (list obj))
        (setf (collector-tail c) (cdr (collector-tail c)))
        )
      
      ; Make it the head and the tail
      (progn
        (setf (collector-tail c) (list obj))
        (setf (collector-head c) (collector-tail c))
        )
      )
    
   ; Return the full list, in case this is the last form of a loop, in
which case the
   ; caller conveniently returns the list they were collecting.
   (collector-head c)
   )

(defun extract-symbols (list &aux (collector (make-collector)))
   "Return a list of all the symbols in a list (a test function for the
collector)"
   (declare (special collector))
   (mapl 
     #'(lambda (lobj &aux (obj (car lobj))) (when (symbolp obj) (collect
collector obj)))
     list
     )
   (collector-head collector)
   )

; (extract-symbols '(a b 1.2 "string" c (ignore this) d)) => (A B C D)

; CU
; Dobes
From: Tim Bradshaw
Subject: Re: Please help
Date: 
Message-ID: <nkj4smq5cuw.fsf@tfeb.org>
·········@BJJ.Org (Don Geddis) writes:

> In article <···············@tfeb.org>, Tim Bradshaw wrote:
> > This is fine, if you have a loop.  [...]  there is this magic COLLECTING
> > thing which solves the problem (destroys their model of how lists work, and
> > anyway is not in CL so they get confused about what is in the language).
> 
> But LOOP is in Common Lisp, so why don't you use/teach that?

Well, like it says, `this is fine if you have a loop'.  Apart from
that, teaching LOOP to students starting CL is an insane idea: `here's
this language with a syntax you probably find a bit odd and
difficult. Oh, and by the way, there's this other embedded language
with a completely different syntax, which you have to learn too'.

--tim
From: Joerg-Cyril Hoehle
Subject: Re: Please help
Date: 
Message-ID: <qkp1zhvp8f7.fsf@tzd.dont.telekom.spam.de.me>
"Christopher J. Vogt" <····@computer.org> writes:
> > (let ((acc nil))
> >    (dolist (.....)
> >       ......
> >       (push item acc))
> >    (nreverse acc))
> 
> Gack!  I hope nobody is really using such an idiom, push/reverse is
> unnecessary, and awfully inefficient.  I'm a loop user not a do user so I can't
> give the code for doing this using do, but using loop:
> (loop for item from 1 to 10
>       collect item)
> which is no doubt desctructive, keeping a pointer to the end of 
> the list and using rplacd to add to the end of the list.

While I don't like push+nreverse myself, I cannot agree that this is
inefficient or that loop is faster.  loop has to do very similar work
(destructive update) in this case, like you observe (remember that
nreverse can do its job in one pass).  Whether this is done when an
element is collected or after all have been collected is IMHO much
more architecture- and cache- than language-dependent.

Regards,
	Jorg Hohle
Telekom Research Center -- SW-Reliability
From: Barry Margolin
Subject: Re: Please help
Date: 
Message-ID: <h%5P2.409$kM2.47080@burlma1-snr2>
In article <···············@tzd.dont.telekom.spam.de.me>,
Joerg-Cyril Hoehle <············@tzd.dont.telekom.spam.de.me> wrote:
>While I don't like push+nreverse myself, I cannot agree that this is
>inefficient or that loop is faster.  loop has to do very similar work
>(destructive update) in this case, like you observe (remember that
>nreverse can do its job in one pass).  Whether this is done when an
>element is collected or after all have been collected is IMHO much
>more architecture- and cache- than language-dependent.

I'll wager that almost all LOOP and COLLECTING macros implement this by
maintaining a tail pointer and RPLACDing it as new elements are collected.
This is the type of thing that's a pain to write each time you do it, but
macro implementers are practically *expected* to do it for us.  There are
very few architectures where this isn't the best way to do it; this
technique won't generate cdr-coded lists, but neither will the
push+nreverse.

-- 
Barry Margolin, ······@bbnplanet.com
GTE Internetworking, Powered by BBN, Burlington, MA
*** DON'T SEND TECHNICAL QUESTIONS DIRECTLY TO ME, post them to newsgroups.
Please DON'T copy followups to me -- I'll assume it wasn't posted to the group.
From: Tim Bradshaw
Subject: Re: Please help
Date: 
Message-ID: <nkj67765dpn.fsf@tfeb.org>
Barry Margolin <······@bbnplanet.com> writes:

> I'll wager that almost all LOOP and COLLECTING macros implement this by
> maintaining a tail pointer and RPLACDing it as new elements are collected.
> This is the type of thing that's a pain to write each time you do it, but
> macro implementers are practically *expected* to do it for us.  There are
> very few architectures where this isn't the best way to do it; this
> technique won't generate cdr-coded lists, but neither will the
> push+nreverse.
> 

However it does involve repeatedly destructively modifying objects
(rather than bindings like PUSH) which may be being tenured by the
garbage collector, leaving you with pionters from older generations
into newer ones, and thus I think, giving the GC more work to do.

I had at least one instance where I spent a long time doing something
like this and it ended up causing a lot of mysterious GC stuff to
happen and was actually not faster than what I did originally.  OTOH
this was a while ago and I forget the fine details, so I may be just
wrong.

--tim
From: Lieven Marchand
Subject: Re: Please help
Date: 
Message-ID: <m37lrnapuq.fsf@localhost.localdomain>
"Christopher J. Vogt" <····@computer.org> writes:

> Lieven Marchand wrote:
> > (let ((acc nil))
> >    (dolist (.....)
> >       ......
> >       (push item acc))
> >    (nreverse acc))
> 
> Gack!  I hope nobody is really using such an idiom, push/reverse is
> unnecessary, and awfully inefficient.  I'm a loop user not a do user so I can't
> give the code for doing this using do, but using loop:
> (loop for item from 1 to 10
>       collect item)
> which is no doubt desctructive, keeping a pointer to the end of 
> the list and using rplacd to add to the end of the list.
> 

Beauty is in the eye of the beholder but regarding efficiency:
did you try to time this? It's certainly not awfully inefficient.

USER(1): (declaim (optimize (safety 0 space 0 speed 3 debug 0)))
T
USER(2): (defvar *master-list* (loop for i from 1 to 10000 collect i))
*MASTER-LIST*
USER(4): (defun test-1 ()
	   (dotimes (i 100)
	     (let ((acc nil))
	       (dolist (item *master-list*)
		 (push (1+ item) acc))
	       (nreverse acc))))
TEST-1
USER(5): (compile 'test-1)
TEST-1
NIL
NIL
USER(6): (time (test-1))
; cpu time (non-gc) 410 msec user, 0 msec system
; cpu time (gc)     210 msec user, 0 msec system
; cpu time (total)  620 msec user, 0 msec system
; real time  645 msec
; space allocation:
;  1,000,000 cons cells, 0 symbols, 32 other bytes, 0 static bytes
NIL
USER(8): (defun test-2 ()
	   (dotimes (i 100)
	     (loop for item in *master-list*
		   collect (1+ item))))
TEST-2
USER(9): (compile 'test-2)
TEST-2
NIL
NIL
USER(10): (time (test-2))
; cpu time (non-gc) 310 msec user, 0 msec system
; cpu time (gc)     110 msec user, 0 msec system
; cpu time (total)  420 msec user, 0 msec system
; real time  428 msec
; space allocation:
;  1,000,100 cons cells, 0 symbols, 32 other bytes, 0 static bytes
NIL

So yes, in this implementation (Allegro CL 5.0 on Linux) LOOP is
faster but I had to augment the length of the list to 10000 to get
measurable differences.

An interesting difference is that the LOOP version uses one additional
cons to do its work.

-- 
Lieven Marchand <···@bewoner.dma.be>
If there are aliens, they play Go. -- Lasker
From: Paolo Amoroso
Subject: Re: Please help
Date: 
Message-ID: <3710f409.3200922@news.mclink.it>
On 06 Apr 1999 21:53:27 +0200, Lieven Marchand <···@bewoner.dma.be> wrote:

> In many idiomatic uses of destructive operations, there isn't any
> effort to be invested. For instance,
> 
> (let ((acc nil))
>    (dolist (.....)
>       ......
>       (push item acc))
>    (nreverse acc))
> 
> is an idiomatic way to collect some stuff together. NREVERSE is
> destructive but the function this is part of is still purely
> functional since it only destructively modifies things it has itself
> created.

This is what Paul Graham calls "functional behavior" in his book "On Lisp".
I don't have it handy, but I seem to remember that there's a chapter with a
similar title.


Paolo
-- 
Paolo Amoroso <·······@mclink.it>
From: Steven M. Haflich
Subject: Re: Please help
Date: 
Message-ID: <36FA9C70.9791A372@franz.com>
Marco Antoniotti wrote:

> (defun duplic8 (the-list element)
>    (loop for x in the-list
>          if (eql x element)
>            append (list x x)
>          else
>            append (list x)))

If you like loop, I like this better:

(defun duplic8 (the-list element)
  (loop for x in the-list
      collect x
      when (eql x element) collect x))
From: Marco Antoniotti
Subject: Re: Please help
Date: 
Message-ID: <lwpv5w7f32.fsf@copernico.parades.rm.cnr.it>
"Steven M. Haflich" <···@franz.com> writes:

> Marco Antoniotti wrote:
> 
> > (defun duplic8 (the-list element)
> >    (loop for x in the-list
> >          if (eql x element)
> >            append (list x x)
> >          else
> >            append (list x)))
> 
> If you like loop, I like this better:
> 
> (defun duplic8 (the-list element)
>   (loop for x in the-list
>       collect x
>       when (eql x element) collect x))

Beautiful!

-- 
Marco Antoniotti ===========================================
PARADES, Via San Pantaleo 66, I-00186 Rome, ITALY
tel. +39 - 06 68 10 03 17, fax. +39 - 06 68 80 79 26
http://www.parades.rm.cnr.it/~marcoxa
From: Will Fitzgerald
Subject: debugging anonymous lambda forms
Date: 
Message-ID: <7dg20f$am1@news.net-link.net>
This began as a reaction to:

>> Marco Antoniotti wrote:
>>
>> > (defun duplic8 (the-list element)
>> >    (loop for x in the-list
>> >          if (eql x element)
>> >            append (list x x)
>> >          else
>> >            append (list x)))
>>
>> If you like loop, I like this better:
>>
>> (defun duplic8 (the-list element)
>>   (loop for x in the-list
>>       collect x
>>       when (eql x element) collect x))
>

There's a question at the end of this longish response--please keep
reading...


I've begun writing functions like the above more and more like this:

(defun duplic9 (list predicate)
  (cond
    ((null list) '())
    ((funcall predicate (car list))
     (cons (car list) (duplic8 (cdr list) predicate)))
    (t (duplic9 (cdr list) predicate)))))

or the loop version:

(defun duplic9 (list predicate)
  (loop for x in list
     collect x
     when (funcall predicate x) collect x))


based on a comment--I think by Olin Shivers in discussions about a standard
list library for Scheme--that when you see a predicate like EQL embedded in
a function, you're not capturing the right abstraction--it's like having any
other constant  embedded in your function.

Of course, to capture the exact semantics of the original duplic8 function,
you would call, for example:

(duplic9 '(a b c d e) #'(lambda (item) (eql item 'd)))

or write duplic8 in terms of duplic9:

(defun duplic8 (list elt)
  (duplic9 list #'(lambda (item) (eql elt item))))

Here's the question/comment: Using all of these anonymous lambdas are fun
and all, but they cause a real headache while debugging. The environments I
tend to use, at least, give very cryptic comments when errors occur with
these, things like:

ERROR: NIL can't be funcalled or applied, in an anonymous lambda form inside
an anonymous lambda form, inside an anonymous lambda form.

Backtracing shows lots of anonymous lambda forms...

Seriously, what tricks do people have for debugging in the face of lots of
anonymous lambdas?

Will Fitzgerald
From: Erik Naggum
Subject: Re: debugging anonymous lambda forms
Date: 
Message-ID: <3131451053382297@naggum.no>
* "Will Fitzgerald" <··········@neodesic.com>
| based on a comment--I think by Olin Shivers in discussions about a
| standard list library for Scheme--that when you see a predicate like EQL
| embedded in a function, you're not capturing the right abstraction--it's
| like having any other constant embedded in your function.

  Scheme has to do it that way because it has so weak lambda lists, with
  neither optional nor keyword arguments, and no macro system to build
  them, either.

  I prefer to use KEY and TEST keyword arguments that default to #'IDENTITY
  and #'EQL.

(defun duplic8 (element list &key (test #'eql) (key #'identity))
  (loop for x in list
     collect x
     when (funcall test (funcall key x) element) collect x))

  incidentally, "any other constant embedded in your function" does not
  sound like a real argument, since functions by their very nature must
  have a number of constants.

#:Erik
From: Will Fitzgerald
Subject: Re: debugging anonymous lambda forms
Date: 
Message-ID: <7dh4f0$bnv@news.net-link.net>
>* "Will Fitzgerald" <··········@neodesic.com>
>| based on a comment--I think by Olin Shivers in discussions about a
>| standard list library for Scheme--that when you see a predicate like EQL
>| embedded in a function, you're not capturing the right abstraction--it's
>| like having any other constant embedded in your function.
>
>  Scheme has to do it that way because it has so weak lambda lists, with
>  neither optional nor keyword arguments, and no macro system to build
>  them, either.
>
>  I prefer to use KEY and TEST keyword arguments that default to #'IDENTITY
>  and #'EQL.
>
>(defun duplic8 (element list &key (test #'eql) (key #'identity))
>  (loop for x in list
>     collect x
>     when (funcall test (funcall key x) element) collect x))
>
>  incidentally, "any other constant embedded in your function" does not
>  sound like a real argument, since functions by their very nature must
>  have a number of constants.
>
>#:Erik

Well, I didn't say it very well--all I meant was that it makes sense for the
equality test not be a constant embedded in the function, but should be a
parameter to the function, since the nature of the 'duplic8' function was to
collect equivalent things. And, and KMP has taught us all by now, one
equivalence predicate just isn't enough.

Shiver's original statement was:

"I usually take a commitment to a particular equality predicate as a
sign of poor design."

embedded within
<http://srfi.schemers.org/srfi-1/mail-archive/msg00033.html>. So you can see
I was stretching his comments a bit.
From: David B. Lamkins
Subject: Re: debugging anonymous lambda forms
Date: 
Message-ID: <v_OK2.18552$A6.9856668@news1.teleport.com>
In article <··········@news.net-link.net> , "Will Fitzgerald" 
<··········@neodesic.com> wrote:

[snip]

> Here's the question/comment: Using all of these anonymous lambdas are fun
> and all, but they cause a real headache while debugging. The environments I
> tend to use, at least, give very cryptic comments when errors occur with
> these, things like:
>
> ERROR: NIL can't be funcalled or applied, in an anonymous lambda form inside
> an anonymous lambda form, inside an anonymous lambda form.
>
> Backtracing shows lots of anonymous lambda forms...
>
> Seriously, what tricks do people have for debugging in the face of lots of
> anonymous lambdas?

Alan Ruttenberg wrote a patch for MCL that names anonymous lambdas in a way
similar to what Kent has described in this thread.

(defun foo ()
  (mapc #'(lambda (x) (break) x)
        (list 1 2 3)))

(foo)
> Break:
> While executing: (:LAMBDA 1 :IN FOO)
> Type Command-/ to continue, Command-. to abort.
> If continued: Return from BREAK.
See the Restarts� menu item for further choices.
1 >

Look for anonymous-not!.lisp on
<http://alanr.www.media.mit.edu/people/alanr/dev+.html>.

--
David B. Lamkins <http://www.teleport.com/~dlamkins/>

Wintel is the Yugo of the computer world: cheap to buy, costly to keep.
From: Vassil Nikolov
Subject: Re: debugging anonymous lambda forms
Date: 
Message-ID: <7dgqbb$c8u$1@nnrp1.dejanews.com>
In article <··········@news.net-link.net>,
  "Will Fitzgerald" <··········@neodesic.com> wrote:
(...)
> ERROR: NIL can't be funcalled or applied, in an anonymous lambda form inside
> an anonymous lambda form, inside an anonymous lambda form.
>
> Backtracing shows lots of anonymous lambda forms...

Not that I put much hope in this, but just for completeness,
does it get better with (OPTIMIZE DEBUG)?

Vassil Nikolov <········@poboxes.com> www.poboxes.com/vnikolov
(You may want to cc your posting to me if I _have_ to see it.)
   LEGEMANVALEMFVTVTVM  (Ancient Roman programmers' adage.)

-----------== Posted via Deja News, The Discussion Network ==----------
http://www.dejanews.com/       Search, Read, Discuss, or Start Your Own    
From: Jason Trenouth
Subject: Re: debugging anonymous lambda forms
Date: 
Message-ID: <37638e5c.766515562@newshost>
On Fri, 26 Mar 1999 08:26:34 -0500, "Will Fitzgerald"
<··········@neodesic.com> wrote:

...

> Seriously, what tricks do people have for debugging in the face of lots of
> anonymous lambdas?
> 
> Will Fitzgerald
> 

One trick is to use lists instead of lambdas:

i.e instead of

	(funcall callback arg)

	(apply (car callback) arg (cdr callback))

Then instead of

	#'(lambda (x) (foo x y)

you use

	`(foo ,y)

This sometimes makes it easier to do debugging.

(Excuse the untested lisp)

__Jason
From: Kent M Pitman
Subject: Re: debugging anonymous lambda forms
Date: 
Message-ID: <sfwiubos5vf.fsf@world.std.com>
"Will Fitzgerald" <··········@neodesic.com> writes:

> Backtracing shows lots of anonymous lambda forms...
> 
> Seriously, what tricks do people have for debugging in the face of lots of
> anonymous lambdas?

Well, most Lisps name their anonymous lambdas better.
My recollection is that Genera compiled duplic8 above as if you'd done
 (duplic9 list #'(:internal duplic8 0))
Genera had a generalized notion of function names based on compounds,
so that #'(:internal (:internal foo 0) 3) would mean the fourth
definition in the first definition in foo, for example.  This encapsulation
facility is also used to support tracing, etc. Other implementations
might flatten the names to symbols, but you should send bug reports
if they can't give you any debugging info at all.

Even with that, you sometimes also want:
 (defun make-tester (item)
   #'(lambda (test) (eql item test)))
So that you call (foo (make-tester 3)) instead of 
directly calling the closure so that you see 
 (:internal make-tester 0)
instead of 
 (:internal function-needing-the-test 0)
This also potentially increases sharing.  But if lambdas give you
junky names in the debugger, you're pretty much stuck.

Bug reports and your willingness to change vendors are critical in
cases like this.
From: Thomas A. Russ
Subject: Re: Please help
Date: 
Message-ID: <ymi90clnxn6.fsf@sevak.isi.edu>
Marco Antoniotti <·······@copernico.parades.rm.cnr.it> writes:
 
A few simpler Loop examples:

(defun duplic8 (the-list element)
   (loop for x in the-list
         if (eql x element)
           collect x and collect x
         else 
           collect x))

(defun duplic8 (the-list element)
   (loop for x in the-list
         collect x         ; Always
         when (eql x element)
           collect x))

-- 
Thomas A. Russ,  USC/Information Sciences Institute          ···@isi.edu