From: Johan
Subject: Multiple arguments to mapcar?
Date: 
Message-ID: <1194306741.124332.122800@v23g2000prn.googlegroups.com>
I want to map over a list, but to treat the first object differently
from the rest. My previous experience with Lisp was Interlisp, which
has dynamic binding. In Interlisp I could code my problem similar to
this:

(defun foo ()
  (let ((first-time t))
    (mapcar #'bar '(1 2))))

(defun bar (n)
  (cond
    (first-time
     (setf first-time nil)
     (+ n 1))
    (t (+ n 2))))

and (foo) would return (2 4). This doesn't work with lexical binding,
and I don't want to make first-time a global variable. What is the
best design pattern in Common Lisp?

From: ········@hpc.unm.edu
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <1194315062.690388.295240@z9g2000hsf.googlegroups.com>
You could use dynamic variable binding - just like your interlisp
example.

(defun foo ()
  (let ((first-time t))
    (declare (special first-time))
    (mapcar #'bar '(1 2))))

(defun bar (n)
  (declare (special first-time))
  (cond
    (first-time
     (setf first-time nil)
     (+ n 1))
    (t (+ n 2))))

(foo)
=>
(2 4)

Jim

On Nov 5, 4:52 pm, Johan <············@gmail.com> wrote:
> I want to map over a list, but to treat the first object differently
> from the rest. My previous experience with Lisp was Interlisp, which
> has dynamic binding. In Interlisp I could code my problem similar to
> this:
>
> (defun foo ()
>   (let ((first-time t))
>     (mapcar #'bar '(1 2))))
>
> (defun bar (n)
>   (cond
>     (first-time
>      (setf first-time nil)
>      (+ n 1))
>     (t (+ n 2))))
>
> and (foo) would return (2 4). This doesn't work with lexical binding,
> and I don't want to make first-time a global variable. What is the
> best design pattern in Common Lisp?
From: Alan Crowe
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <86r6j3p5f6.fsf@cawtech.freeserve.co.uk>
········@hpc.unm.edu writes:

> You could use dynamic variable binding - just like your interlisp
> example.
> 
> (defun foo ()
>   (let ((first-time t))
>     (declare (special first-time))
>     (mapcar #'bar '(1 2))))
> 
> (defun bar (n)
>   (declare (special first-time))
>   (cond
>     (first-time
>      (setf first-time nil)
>      (+ n 1))
>     (t (+ n 2))))
> 
> (foo)
> =>
> (2 4)
> 

I second this suggestion. Dynamic variables are
under-appreciated. Common Lisp really has three disciplines
for scope and extent, lexical, binding-by-binding specials
and symbol-by-symbol specials.

The usual way of teaching Common Lisp starts with a binary
split:

lexical versus both kinds of special.

Then symbol-by-symbol specials get taught, (defvar *x*),
(defparameter *y*) and binding-by-binding specials get
forgotten about.

It might be better to start with a different binary split

symbol-by-symbol versus binding-by-binding

Then under binding-by-binding one learns that 
(declare (special x)) plays nicely with closures. That is to
say 

CL-USER> (let ((x 'special))
           (declare (special x))
           (funcall (let ((x 'lexical))
                      (lambda() x))))
LEXICAL

Although (lambda() x) gets called outside the extent of 
(let ((x 'lexical))...) it is still a lexical binding
binding because (declare (special x)), unlike 
(proclaim '(special x)), is not pervasive.

So one may use binding-by-binding specials freely, without
*earmuffs*, because they don't introduce hard-to-find bugs
by breaking closures unexpectedly.

Alan Crowe
Edinburgh
Scotland
From: Thomas A. Russ
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <ymive8e28en.fsf@blackcat.isi.edu>
Alan Crowe <····@cawtech.freeserve.co.uk> writes:

> ········@hpc.unm.edu writes:
> 
> > You could use dynamic variable binding - just like your interlisp
> > example.
> > 
> > (defun foo ()
> >   (let ((first-time t))
> >     (declare (special first-time))
> >     (mapcar #'bar '(1 2))))
> > 
> > (defun bar (n)
> >   (declare (special first-time))
> >   (cond
> >     (first-time
> >      (setf first-time nil)
> >      (+ n 1))
> >     (t (+ n 2))))

> I second this suggestion. Dynamic variables are
> under-appreciated. Common Lisp really has three disciplines
> for scope and extent, lexical, binding-by-binding specials
> and symbol-by-symbol specials.

[snip]

> CL-USER> (let ((x 'special))
>            (declare (special x))
>            (funcall (let ((x 'lexical))
>                       (lambda() x))))
> LEXICAL
> 
> Although (lambda() x) gets called outside the extent of 
> (let ((x 'lexical))...) it is still a lexical binding
> binding because (declare (special x)), unlike 
> (proclaim '(special x)), is not pervasive.
> 
> So one may use binding-by-binding specials freely, without
> *earmuffs*, because they don't introduce hard-to-find bugs
> by breaking closures unexpectedly.

Although I'm largely in agreement with Alan Crowe's analysis, there is
still the potential (inherent in dynamic binding) to get interactions.
In some ways this might be even more obscure with the binding-by-binding
specials than with the global special variables.

In my solution, I was still tempted to add the asterisks to the name.

The way this works is when you get two sets of interacting code that
happen to use the same variable name to control behavior.  This gets
exacerbated if one uses a common sort of name like "first-time":

 (defun foo ()
   (let ((first-time t))
     (declare (special first-time))
     (mapcar #'bar (mapcar #'baz '(1 2)))))
 
 (defun bar (n)
   (declare (special first-time))
   (cond
     (first-time
      (setf first-time nil)
      (+ n 1))
     (t (+ n 2))))

 (defun baz (n)
   (declare (special first-time))
   (cond
     (first-time
      (setf first-time nil)
      (* n n))
     (t (* n 2))))

This will, of course, be a lot more obscure if it is, in fact, some
function called in BAR that internally invokes BAZ.  Then there will
potentially be some clobbering going on.  This can be controlled
somewhat by using LET binding to limit the dynamic scope of particular
variables, but it is still possible to have some side effects creep in.

But since that is the nature of dynamic binding, there isn't anything
that can be done about it.  It afflicts the binding-by-binding form just
like the global specials -- although one might be more tempted to just
use SETQ on global special variables rather than binding them in LET
blocks.

In any case, one nice bonus of the binding-by-binding form is that if
you invoke it in a dynamic scope outside of its binding form, you get an
unbound variable error, unlike having the default value of NIL (which
you get from DEFVAR) made available.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Alan Crowe
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <86r6j2m07j.fsf@cawtech.freeserve.co.uk>
···@sevak.isi.edu (Thomas A. Russ) writes:

> Although I'm largely in agreement with Alan Crowe's analysis, there is
> still the potential (inherent in dynamic binding) to get interactions.
> In some ways this might be even more obscure with the binding-by-binding
> specials than with the global special variables.
> 
> In my solution, I was still tempted to add the asterisks to the name.
> 
> The way this works is when you get two sets of interacting code that
> happen to use the same variable name to control behavior.  This gets
> exacerbated if one uses a common sort of name like "first-time":
> 
>  (defun foo ()
>    (let ((first-time t))
>      (declare (special first-time))
>      (mapcar #'bar (mapcar #'baz '(1 2)))))
>  
>  (defun bar (n)
>    (declare (special first-time))
>    (cond
>      (first-time
>       (setf first-time nil)
>       (+ n 1))
>      (t (+ n 2))))
> 
>  (defun baz (n)
>    (declare (special first-time))
>    (cond
>      (first-time
>       (setf first-time nil)
>       (* n n))
>      (t (* n 2))))

That is a interesting example. I've tended to imagine
special variables being used in a purely function style,
with variables being rebound but not assigned. The effect of
rebinding is limited to the extent of the inner binding
form, which offers a good chance that examples of
interaction-by-name are limited to those that the programmer
intended.

If however the variable is assigned to, then there isn't an
inner binding and the side-effect is effective for the
remainder of the extent of the outer binding form. There is
much more scope for problems.

If a dynamic variable is used for passing information back
to the calling scope it will require an assignment to an
outer scope rather than a binding in an inner scope, so
dynamic variables are perhaps more dangerous than I've realised.

> In any case, one nice bonus of the binding-by-binding form is that if
> you invoke it in a dynamic scope outside of its binding form, you get an
> unbound variable error, unlike having the default value of NIL (which
> you get from DEFVAR) made available.

The authors of the specification have anticipated your
concern and did in fact specify that DEFVAR behave better
than you say:

   If no initial-value is supplied, defvar leaves the value
   cell of the dynamic variable named name undisturbed; if
   name was previously bound, its old value persists, and if
   it was previously unbound, it remains unbound.

Alan Crowe
Edinburgh
Scotland
From: Steven M. Haflich
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <7AQXi.904$0Q5.534@nlpi070.nbdc.sbc.com>
Johan wrote:
> I want to map over a list, but to treat the first object differently
> from the rest.

You could try thinking outside the box outside the box outside the box 
outside the box ...

cl-user(2): (defun foo ()
	      (mapcar #'bar '(1 2) '(t . #1= (nil . #1#))))
foo
cl-user(3): (defun bar (x first-time)
	      (cond (first-time (+ x 1))
		    (t (+ x 2))))
bar
cl-user(4): (foo)
(2 4)

This is entirely portable CL, although not the most obvious solution to 
your stated problem.
From: Tobias C. Rittweiler
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <87prynpw2a.fsf@freebits.de>
"Steven M. Haflich" <···@alum.mit.edu> writes:

> cl-user(2): (defun foo ()
> 	      (mapcar #'bar '(1 2) '(t . #1= (nil . #1#))))
> foo
> cl-user(3): (defun bar (x first-time)
> 	      (cond (first-time (+ x 1))
> 		    (t (+ x 2))))
> bar
> cl-user(4): (foo)
> (2 4)
>
> This is entirely portable CL, although not the most obvious solution
> to your stated problem.

Not entirely, alas. MAPCAR is allowed (but not required) to signal an
error if one of the passed lists is not a proper list. Although I don't
know of any implementation which does.

  -T.
From: Steven M. Haflich
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <473045BA.1000200@alum.mit.edu>
Tobias C. Rittweiler wrote:
> "Steven M. Haflich" <···@alum.mit.edu> writes:
> 
>> cl-user(2): (defun foo ()
>> 	      (mapcar #'bar '(1 2) '(t . #1= (nil . #1#))))
>> foo
>> cl-user(3): (defun bar (x first-time)
>> 	      (cond (first-time (+ x 1))
>> 		    (t (+ x 2))))
>> bar
>> cl-user(4): (foo)
>> (2 4)
>>
>> This is entirely portable CL, although not the most obvious solution
>> to your stated problem.
> 
> Not entirely, alas. MAPCAR is allowed (but not required) to signal an
> error if one of the passed lists is not a proper list. Although I don't
> know of any implementation which does.

I takle your point, and basically agree with it.  But if I wanted to be 
argumentative I would point out the inconsistency in the ANS.  In 
detail, from the dictionary page of mapcar:

> Should be prepared to signal an error of type type-error if any list is not a proper list.

This definition is from 1.4.2 Error Terminology:

> Should be prepared to signal an error
> 
> This is similar to "should be signaled" except that it does not imply that `extra effort' has to be taken on the part of an operator to discover an erroneous situation if the normal action of that operator can be performed successfully with only `lazy' checking. An implementation is always permitted to signal an error, but even in safe code, it is only required to signal the error when failing to signal it might lead to incorrect results. In unsafe code, the consequences are undefined.
> 
> For example, defining that "find should be prepared to signal an error of type type-error if its second argument is not a proper list" does not imply that an error is always signaled. The form
> 
>  (find 'a '(a b . c))
> 
> must either signal an error of type type-error in safe code, else return A. In unsafe code, the consequences are undefined. By contrast,
> 
>  (find 'd '(a b . c))
> 
> must signal an error of type type-error in safe code. In unsafe code, the consequences are undefined. Also,
> 
>  (find 'd '#1=(a b . #1#))
> 
> in safe code might return nil (as an implementation-defined extension), might never return, or might signal an error of type type-error. In unsafe code, the consequences are undefined.
> 
> Typically, the "should be prepared to signal" terminology is used in type checking situations where there are efficiency considerations that make it impractical to detect errors that are not relevant to the correct operation of the operator. 

Thinking as an implementor, I cannot imagine a way to detect a circular 
list passed to any of the mapmumble functions that does not require 
"extra effort."  Thinking as one of the X3J13 members who passed the ANS 
for CL, I can be even more sure that no one actually thought about the 
details of the mapcar specification of concern in the current thread. 
It seems to me that the proper list requirement was intended to cover 
the behavior of a dotted list, not a circular list.

(However, I do agree that the behavior of mapcar on a circular list is 
at least undefined by the ANS (again see 1.4.2) and undefined behavior 
includes signaling an error...)

The only argument I can imagine against a relaxation of the requirements 
of noncircular lists to the mapcar would be if there were some plausible 
compiler optimization that would depend upon the receiving a proper 
list.  I can imagine such an optimizer for a list argument that is a 
constant visible at compile time, but such optimizations probably aren't 
very important compared to the elegance of permitting circular lists.
From: Thomas A. Russ
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <ymiabpr2q2c.fsf@blackcat.isi.edu>
"Steven M. Haflich" <···@alum.mit.edu> writes:

> It
> seems to me that the proper list requirement was intended to cover the
> behavior of [mapcar on] a dotted list, not a circular list.

I would have to agree with this.  It is easy to see where a dotted list
could cause problems with the algorithm.  Circular lists would only pose
problems if ALL the list  arguments were circular.  

But I bet users would be disappointed in the performance of MAP... if it
actually performed circular list checking of its arguments.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Tobias C. Rittweiler
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <87zlxr2u3o.fsf@freebits.de>
"Steven M. Haflich" <···@alum.mit.edu> writes:

> Tobias C. Rittweiler wrote:
> > "Steven M. Haflich" <···@alum.mit.edu> writes:
> >
> > > cl-user(2): (defun foo ()
> > > 	      (mapcar #'bar '(1 2) '(t . #1= (nil . #1#))))
> > > ...
> > > This is entirely portable CL, although not the most obvious solution
> > > to your stated problem.
> >
> > Not entirely, alas. MAPCAR is allowed (but not required) to signal an
> > error if one of the passed lists is not a proper list. Although I don't
> > know of any implementation which does.
>
> I takle your point, and basically agree with it.  But if I wanted to
> be argumentative I would point out the inconsistency in the ANS.  In
> detail, from the dictionary page of mapcar:
>
>   ``Should be prepared to signal an error of type type-error if any
>   list is not a proper list.''
>
> This definition is from 1.4.2 Error Terminology: [...]
>
> Thinking as an implementor, I cannot imagine a way to detect a
> circular list passed to any of the mapmumble functions that does not
> require "extra effort."  

You can maintain hoare pointers for every list passed. Of course, this
will not detect any possible case (i.e. the case where one of the passed
lists exhausts before a hoare reaches its respective turtle)---but, and
this is its ontological point, it'll detect the disastrous cases where
only circular lists are passed. Efficiency-wise this scheme means very
minor extra effort.

So an implementation may pursuit this scheme in safe code, and signal an
error for your above example. :-)


> Thinking as one of the X3J13 members who
> passed the ANS for CL, I can be even more sure that no one actually
> thought about the details of the mapcar specification of concern in
> the current thread. It seems to me that the proper list requirement
> was intended to cover the behavior of a dotted list, not a circular
> list.

Notice, especially, that LOOP's for-as-in-list clauses are in
comparasion specified to work on cirular lists as expected.

  -T.
From: Kamen TOMOV
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <uve8fr3y9.fsf@cybuild.com>
On Tue, Nov 06 2007, Steven M. Haflich wrote:

> Johan wrote:
>> I want to map over a list, but to treat the first object differently
>> from the rest.
>
> You could try thinking outside the box outside the box outside the box
> outside the box ...
>
> cl-user(2): (defun foo ()
> 	      (mapcar #'bar '(1 2) '(t . #1= (nil . #1#))))
> foo
> cl-user(3): (defun bar (x first-time)
> 	      (cond (first-time (+ x 1))
> 		    (t (+ x 2))))
> bar
> cl-user(4): (foo)
> (2 4)

Beautiful. Nevertheless, if one needs a function that looks like
mapcar, but enables him/her to treat the elements of the list
differently depending on their index in the list, the following could
be used:

CL-USER> (defun single-list-mapcar-with-counter (fn list)
	   (let ((i 0))
	     (labels ((xx (list)
			(if list
			    (cons (apply fn (list (car list) (incf i)))
				  (xx (cdr list))))))
	       (xx list))))

-- 
Kamen
From: Carl Taylor
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <5MOXi.55100$kj1.14400@bgtnsc04-news.ops.worldnet.att.net>
Johan wrote:
> I want to map over a list, but to treat the first object differently
> from the rest. 

You could use <loop> with the for ... then clause:

CL-USER 7 > 
(defun foo ()
  (loop with xlist = '(1 2 3 4)
        for elem in xlist
        for xvalue = 99 then 2
        collect (+ elem xvalue)))
FOO

CL-USER 8 > (foo)
(100 4 5 6)


Carl Taylor
From: Kamen TOMOV
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <u3avkz0ng.fsf@cybuild.com>
On Tue, Nov 06 2007, Johan wrote:

> I want to map over a list, but to treat the first object differently
> from the rest. My previous experience with Lisp was Interlisp, which
> has dynamic binding. In Interlisp I could code my problem similar to
> this:
>
> (defun foo ()
>   (let ((first-time t))
>     (mapcar #'bar '(1 2))))
>
> (defun bar (n)
>   (cond
>     (first-time
>      (setf first-time nil)
>      (+ n 1))
>     (t (+ n 2))))
>
> and (foo) would return (2 4). This doesn't work with lexical binding,
> and I don't want to make first-time a global variable. What is the
> best design pattern in Common Lisp?
>

Will lambda suit your case? 

         (defun foo ()
	   (let ((first-time t))
	     (mapcar #'(lambda (arg) 
			 (if first-time
			     (progn (setq first-time nil)
				    (1+ arg))
			     (+ 2 arg)))
		     '(1 2))))


-- 
Kamen
From: Johan
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <1194308875.504023.69370@q3g2000prf.googlegroups.com>
On Nov 6, 9:18 am, Kamen TOMOV <·····@cybuild.com> wrote:
> On Tue, Nov 06 2007, Johan wrote:
> Will lambda suit your case?
>
>          (defun foo ()
>            (let ((first-time t))
>              (mapcar #'(lambda (arg)
>                          (if first-time
>                              (progn (setq first-time nil)
>                                     (1+ arg))
>                              (+ 2 arg)))
>                      '(1 2))))
>
> --
> Kamen
Yes, I've noticed that this will work, but I get ugly warnings in
SBCL, and the real function will is too big to define inside foo. I
could of course simply make it a #'(lambda (arg) (if first-time (progn
(setf first-time nil) (do-this arg)) (do-that arg))) but I feel there
should be a better way in Common Lisp. Maybe I don't need first-time
at all?
From: Richard M Kreuter
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <873avkc9q2.fsf@progn.net>
Johan <············@gmail.com> writes:
> On Nov 6, 9:18 am, Kamen TOMOV <·····@cybuild.com> wrote:
>> On Tue, Nov 06 2007, Johan wrote:
>>
>>> I want to map over a list, but to treat the first object
>>> differently from the rest.
>>
>> Will lambda suit your case?
>>
>>          (defun foo ()
>>            (let ((first-time t))
>>              (mapcar #'(lambda (arg)
>>                          (if first-time
>>                              (progn (setq first-time nil)
>>                                     (1+ arg))
>>                              (+ 2 arg)))
>>                      '(1 2))))
>
> Yes, I've noticed that this will work, but I get ugly warnings in
> SBCL, and the real function will is too big to define inside foo. I
> could of course simply make it a #'(lambda (arg) (if first-time
> (progn (setf first-time nil) (do-this arg)) (do-that arg))) but I
> feel there should be a better way in Common Lisp. Maybe I don't need
> first-time at all?

How about this:

(defun foo ()
  (let ((list (list 1 2 3)))
    (cons (bar (first list) :first-time-p t)
          (mapcar #'bar (rest list)))))

(defun bar (thing &key first-time-p)
  (if first-time-p
      <first-time-code>
      <rest-code>))

I'd say it depends on how interesting the callee (BAR) is in itself:
if it exists just for one list mapping, then it's more or less
irrelevant what you do to make work (dynamic variables, extra
arguments, lexical closures, etc.).  If it's an important operator in
your program, then IMO it's worthwhile to make it more functional
(i.e., have its result depend solely on its arguments, and not on any
out-of-band communication, except maybe for documented user
customization variables).  If one of the first-time or non-first-time
behaviors is a unique case and the other behavior is used elsewhere,
maybe break it into two functions, e.g.,

(defun foo ()
  (let ((list (list 1 2 3)))
    (cons (baz (first list))
          (mapcar #'quux (rest list)))))

(defun baz (thing)
  <first element code>)

(defun quux (thing)
  <non-first element code>)

and then you can use the two separate functions freely.

--
RmK
From: Kamen TOMOV
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <uwsswxkk2.fsf@cybuild.com>
On Tue, Nov 06 2007, Johan wrote:

> Yes, I've noticed that this will work, but I get ugly warnings in
> SBCL,

You shouldn't get warnings.

> and the real function will is too big to define inside foo. 

The question, actually, should be if bar is called from other
forms. If not - you can use flet for it.

> I could of course simply make it a #'(lambda (arg) (if first-time
> (progn (setf first-time nil) (do-this arg)) (do-that arg))) but I
> feel there should be a better way in Common Lisp.

There are plenty of ways. In the worst case you can build your own
map.

> Maybe I don't need first-time at all?

I don't think you can do that with map.

However, you can also use a closure:

         (let ((first-time t))
	   (defun foo ()
	     (let ((lst (mapcar #'bar '(1 2))))
	       (setq first-time t)
	       lst))
	   (defun bar (n)
	     (cond
	       (first-time
		(setf first-time nil)
		(+ n 1))
	       (t (+ n 2)))))
-- 
Kamen
From: Thomas A. Russ
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <ymi640f2pwd.fsf@blackcat.isi.edu>
On Tue, Nov 06 2007, Johan wrote:
 
 > I want to map over a list, but to treat the first object differently
 > from the rest. My previous experience with Lisp was Interlisp, which
 > has dynamic binding. In Interlisp I could code my problem similar to
 > this:
 >
 > (defun foo ()
 >   (let ((first-time t))
 >     (mapcar #'bar '(1 2))))
 >
 > (defun bar (n)
 >   (cond
 >     (first-time
 >      (setf first-time nil)
 >      (+ n 1))
 >     (t (+ n 2))))
 >
 > and (foo) would return (2 4). This doesn't work with lexical binding,
 > and I don't want to make first-time a global variable. What is the
 > best design pattern in Common Lisp?

Well, you can arrange to have dynamic bindings WITHOUT making first time
a global variable.  You just have to use paired special declarations.
There are still the same issues of dynamic bindings and inadvertent
changes in the variable names, but this would work:

 (defun foo ()
   (let ((first-time t))
     (declare (special first time))
     (mapcar #'bar '(1 2))))

 (defun bar (n)
   (declare (special first-time))
   (cond
     (first-time
      (setf first-time nil)
      (+ n 1))
     (t (+ n 2))))


-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Pascal Costanza
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <5paj8fFq7hpfU1@mid.individual.net>
Johan wrote:
> I want to map over a list, but to treat the first object differently
> from the rest. My previous experience with Lisp was Interlisp, which
> has dynamic binding. In Interlisp I could code my problem similar to
> this:
> 
> (defun foo ()
>   (let ((first-time t))
>     (mapcar #'bar '(1 2))))
> 
> (defun bar (n)
>   (cond
>     (first-time
>      (setf first-time nil)
>      (+ n 1))
>     (t (+ n 2))))
> 
> and (foo) would return (2 4). This doesn't work with lexical binding,
> and I don't want to make first-time a global variable. What is the
> best design pattern in Common Lisp?

(loop for n in '(1 2)
       for x = (+ n 1) then (+ n 2)
       collect x)


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: Steven M. Haflich
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <YMWXi.41883$eY.20482@newssvr13.news.prodigy.net>
Pascal:  Here's a neat example of using of loop to solve a 
well-understood problem in a nonobvious way...

(defun primes-below-limit (limit)
   (cons 2
	(loop for i from 3 by 2 below limit
	    unless (loop for p in primes
		       when (integerp (/ i p)) return t)
	    collect i into primes
	    finally (return primes))))

I think this is portable and correct, provided limit >= 2.
From: Pascal Costanza
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <5pb2i5Fqab1bU1@mid.individual.net>
Steven M. Haflich wrote:
> Pascal:  Here's a neat example of using of loop to solve a 
> well-understood problem in a nonobvious way...
> 
> (defun primes-below-limit (limit)
>   (cons 2
>     (loop for i from 3 by 2 below limit
>         unless (loop for p in primes
>                when (integerp (/ i p)) return t)
>         collect i into primes
>         finally (return primes))))
> 
> I think this is portable and correct, provided limit >= 2.

Faaaaar to complicated. ;)

(defun primes-below-limit (limit)
   (cons 2
     (loop for i from 3 by 2 below limit
         unless (loop for p in primes
                      thereis (integerp (/ i p)))
         collect i into primes
         finally (return primes))))


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: szergling
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <1194393349.552447.201420@t8g2000prg.googlegroups.com>
On Nov 6, 8:26 pm, Pascal Costanza <····@p-cos.net> wrote:
> Johan wrote:
> > I want to map over a list, but to treat the first object differently
> > from the rest.

[[ ... ]]

> > What is the
> > best design pattern in Common Lisp?
>
> (loop for n in '(1 2)
>        for x = (+ n 1) then (+ n 2)
>        collect x)
>
> Pascal
>

And incidentally, LOOP's more recent cousin,
iterate, has this construct built in:

CL-USER> (iter (for i below 10)
               (collect i))
(0 1 2 3 4 5 6 7 8 9)

CL-USER> (iter (for i below 10)
               (if (first-time-p)
                   (collect (+ i 1))
                   (collect (+ i 2))))
(1 3 4 5 6 7 8 9 10 11)


Just to provided an alternative, balancing point
of view to LOOP :)
From: Rob Warnock
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <BrednTfo-4qS26zanZ2dnUVZ_qOknZ2d@speakeasy.net>
szergling  <···············@gmail.com> wrote:
+---------------
| Pascal Costanza <····@p-cos.net> wrote:
| > (loop for n in '(1 2)
| >        for x = (+ n 1) then (+ n 2)
| >        collect x)
| 
| And incidentally, LOOP's more recent cousin,
| iterate, has this construct built in:
...
| CL-USER> (iter (for i below 10)
|                (if (first-time-p)
|                    (collect (+ i 1))
|                    (collect (+ i 2))))
| (1 3 4 5 6 7 8 9 10 11)
| 
| Just to provided an alternative, balancing point
| of view to LOOP :)
+---------------

But you didn't really, since you changed the style,
and of course LOOP can do *that* style, too:  ;-}  ;-}

    > (loop for i below 10
	    and first-time = t then nil
	if first-time
	  collect (+ i 1)
	else
	  collect (+ i 2))

    (1 3 4 5 6 7 8 9 10 11)
    > 

or, equivalently:

    > (loop for i below 10
	    and first-time = t then nil
	collect (if first-time
                  (+ i 1)
                  (+ i 2)))

    (1 3 4 5 6 7 8 9 10 11)
    > 

This is not to say that ITERATE doesn't have things which are
better than LOOP, only that (FIRST-TIME-P) isn't one of them.


-Rob

-----
Rob Warnock			<····@rpw3.org>
627 26th Avenue			<URL:http://rpw3.org/>
San Mateo, CA 94403		(650)572-2607
From: szergling
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <1194493326.141348.19250@q5g2000prf.googlegroups.com>
On Nov 7, 5:58 pm, ····@rpw3.org (Rob Warnock) wrote:
> szergling  <···············@gmail.com> wrote:
> | CL-USER> (iter (for i below 10)
> |                (if (first-time-p)
> |                    (collect (+ i 1))
> |                    (collect (+ i 2))))
> | (1 3 4 5 6 7 8 9 10 11)
> |
> | Just to provided an alternative, balancing point
> | of view to LOOP :)
> +---------------
>
> But you didn't really, since you changed the style,
> and of course LOOP can do *that* style, too:  ;-}  ;-}
>
>     > (loop for i below 10
>             and first-time = t then nil
>         if first-time
>           collect (+ i 1)
>         else
>           collect (+ i 2))
>
>     (1 3 4 5 6 7 8 9 10 11)
>     >

You're quite right. I haven't thought through it enough
to realise it can be that simple. Put it this way, iter
can codify a construct more explicitly than loop does,
since it is more extendable.

While we are on LOOP, what do you all think of this style
of LOOP usage:

;; I'm not at work, so this is hypothetical
(with-predictions-loop (setup-vars)
   collect data-x into xs
   collect data-y into ys
   do (plot-add-point data-x data-y)
   finally (return (list xs ys)))

where with-predictions-loop expands to a loop, and splices
body into the final loop clause, ie `(loop ... ,@body).

So aesthetically, is this good, bad, or ugly? (ya, this is
a bit open ended)
From: Marco Antoniotti
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <1194516170.936161.238960@t8g2000prg.googlegroups.com>
On Nov 8, 5:42 am, szergling <···············@gmail.com> wrote:
> On Nov 7, 5:58 pm, ····@rpw3.org (Rob Warnock) wrote:
>
>
>
> > szergling  <···············@gmail.com> wrote:
> > | CL-USER> (iter (for i below 10)
> > |                (if (first-time-p)
> > |                    (collect (+ i 1))
> > |                    (collect (+ i 2))))
> > | (1 3 4 5 6 7 8 9 10 11)
> > |
> > | Just to provided an alternative, balancing point
> > | of view to LOOP :)
> > +---------------
>
> > But you didn't really, since you changed the style,
> > and of course LOOP can do *that* style, too:  ;-}  ;-}
>
> >     > (loop for i below 10
> >             and first-time = t then nil
> >         if first-time
> >           collect (+ i 1)
> >         else
> >           collect (+ i 2))
>
> >     (1 3 4 5 6 7 8 9 10 11)
>
> You're quite right. I haven't thought through it enough
> to realise it can be that simple. Put it this way, iter
> can codify a construct more explicitly than loop does,
> since it is more extendable.

This is to be proven!  Again, the ITERATE extension machinery is not
well documented and not that easy to use.  At least I did not have the
patience to get at it.






>
> While we are on LOOP, what do you all think of this style
> of LOOP usage:
>
> ;; I'm not at work, so this is hypothetical
> (with-predictions-loop (setup-vars)
>    collect data-x into xs
>    collect data-y into ys
>    do (plot-add-point data-x data-y)
>    finally (return (list xs ys)))
>
> where with-predictions-loop expands to a loop, and splices
> body into the final loop clause, ie `(loop ... ,@body).
>
> So aesthetically, is this good, bad, or ugly? (ya, this is
> a bit open ended)

It is not that bad.  That is what I did in CL-ENUMERATIONS (shameless
plug http://common-lisp.net/project/cl-enumeration/) with the FOREACH
macro.  However, what does WITH-PREDICTIONS-LOOP do?

Cheers

Marco
From: John Thingstad
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <op.t1g0mwf3ut4oq5@pandora.alfanett.no>
P� Thu, 08 Nov 2007 11:02:50 +0100, skrev Marco Antoniotti  
<·······@gmail.com>:

>
> This is to be proven!  Again, the ITERATE extension machinery is not
> well documented and not that easy to use.  At least I did not have the
> patience to get at it.
>

?? I had no problem with the documentation. The ANSI spesifaction of loop  
is unreadble..
If it hadn't been for Peter Seibel's "loop for black belters" I think  
no-one would have started using loop.
Seriosly, before that hardly anyone used it (in articles here at least).
For the same reason iterate never took off.
But if you know loop it is fairly simple to transfer that knowlege to  
iterate.
So first read (and understand) "loop for black belters" then read the  
documentation for iterate.
iterate is just a superset of the functionality of loop with a cleaned up  
syntax.
The simplified syntax allows iterate to be constructed by a number of  
macroes rather than a compiler.
This also makes it much easier to extend. In fact ANSI CL dosn't even  
spesify a means to extend loop (most CL implementations allow this, the  
original paper written on loop spesifed a means).

-- 
Sendt med Operas revolusjonerende e-postprogram: http://www.opera.com/mail/
From: Peder O. Klingenberg
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <kswsstt1kc.fsf@beto.netfonds.no>
"John Thingstad" <·······@online.no> writes:

> If it hadn't been for Peter Seibel's "loop for black belters" I think
> no-one would have started using loop.

Rubbish.  I was using loop for years before Peter's book came out, and
I still haven't gotten around to reading that far into PCL.

...Peder...
-- 
This must be Thursday.  I never could get the hang of Thursdays.
From: Raffael Cavallaro
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <2007110815394575249-raffaelcavallaro@pasdespamsilvousplaitmaccom>
On 2007-11-08 06:24:22 -0500, "John Thingstad" <·······@online.no> said:

> If it hadn't been for Peter Seibel's "loop for black belters" I think  
> no-one would have started using loop.
> Seriosly, before that hardly anyone used it (in articles here at least).

This is really an extreme overstatement;

<http://groups.google.com/groups/search?lr=&safe=off&num=10&q=%22loop+for%22+group%3Acomp.lang.lisp&safe=off&qt_s=Search&as_drrb=b&as_mind=1&as_minm=1&as_miny=1981&as_maxd=31&as_maxm=12&as_maxy=2002>

(this 

is a search for the exact phrase "loop for" in c.l.l before the end of 2002)

returns over a thousand articles on the loop macro in c.l.l before 
practical common lisp was written.

Note that searching for the word "lisp" in c.l.l over the same period 
returns only 1500 articles, so 1000 on loop means it was routinely 
discussed in c.l.l before Peter's treatement. (Google groups database 
is much sparser the farther back in time you go).

Many of us have been using loop for years, long before Peter Siebel's 
excellent book existed.
From: szergling
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <1194576774.389848.200320@k35g2000prh.googlegroups.com>
On Nov 8, 11:02 pm, Marco Antoniotti <·······@gmail.com> wrote:
> On Nov 8, 5:42 am, szergling <···············@gmail.com> wrote:

[[ ... ]]

> > You're quite right. I haven't thought through it enough
> > to realise it can be that simple. Put it this way, iter
> > can codify a construct more explicitly than loop does,
> > since it is more extendable.
>
> This is to be proven!  Again, the ITERATE extension
> machinery is not well documented and not that easy to use.
> At least I did not have the patience to get at it.

I tend to agree with John Thingstad's comments. It's not
that different from LOOP, but that doesn't mean anyone can
just pick it up trivially. When I first dove into it, there
really weren't too many differences.

> > ;; I'm not at work, so this is hypothetical
> > (with-predictions-loop (setup-vars)
> >    collect data-x into xs
> >    collect data-y into ys
> >    do (plot-add-point data-x data-y)
> >    finally (return (list xs ys)))

[[...]]

> > where with-predictions-loop expands to a loop, and splices
> > body into the final loop clause, ie `(loop ... ,@body).

[[...]]

> macro.  However, what does WITH-PREDICTIONS-LOOP do?

It's a one-off thing to setup a frequently needed set of
bindings (& iterate over data) for a particular problem I
had. Just a quick hack with limited re-use value.
From: Tobias C. Rittweiler
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <87lk9bpvxs.fsf@freebits.de>
Johan <············@gmail.com> writes:

> I want to map over a list, but to treat the first object differently
> from the rest. My previous experience with Lisp was Interlisp, which
> has dynamic binding. In Interlisp I could code my problem similar to
> this:
>
> (defun foo ()
>   (let ((first-time t))
>     (mapcar #'bar '(1 2))))
>
> (defun bar (n)
>   (cond
>     (first-time
>      (setf first-time nil)
>      (+ n 1))
>     (t (+ n 2))))
>
> and (foo) would return (2 4). This doesn't work with lexical binding,
> and I don't want to make first-time a global variable. What is the
> best design pattern in Common Lisp?

I'd go for simplicity:

  (cons (f (first list)) 
        (mapcar #'g (rest list)))

HTH,

  -T.
From: namekuseijin
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <1194344398.483643.132280@z9g2000hsf.googlegroups.com>
On 6 nov, 07:25, "Tobias C. Rittweiler" <····@freebits.de.invalid>
wrote:
> I'd go for simplicity:
>
>   (cons (f (first list))
>         (mapcar #'g (rest list)))

this is the most sane way.  Thanks for saving me typing it...
From: Kaz Kylheku
Subject: Re: Multiple arguments to mapcar?
Date: 
Message-ID: <1194463260.723576.178250@e9g2000prf.googlegroups.com>
On Nov 5, 3:52 pm, Johan <············@gmail.com> wrote:
> I want to map over a list, but to treat the first object differently
> from the rest. My previous experience with Lisp was Interlisp, which
> has dynamic binding. In Interlisp I could code my problem similar to
> this:
>
> (defun foo ()
>   (let ((first-time t))
>     (mapcar #'bar '(1 2))))
>
> (defun bar (n)
>   (cond
>     (first-time
>      (setf first-time nil)
>      (+ n 1))
>     (t (+ n 2))))

Solution 1: use an additional list in mapcar, to supply an additional
argument to the function:

  (defun bar (n m)
    (+ n m))

  (mapcar #'bar '(1 2 3 4) '(1 2 2 2))

Solution 2a: recognize that the first item is being treated with a de-
facto different function.

  (defun bar-first (n) ...)

  (defun bar-rest (n) ...)

  (cons (bar-first (first list)) (mapcar #'bar-rest (rest list)))

Solution 2b: roll the functions into one.

  (defun bar (n &optional first-p) ...)

  (cons (bar (first list) t) (mapcar #'bar (rest list)))

I like 2a. The purpose of mapcar is to filter the elements of a list
through a function. Your problem in fact calls for special treatment
of the first item, which means that it is in fact being filtered
through a different function from the rest. The ``natural'' solution
is to treat the first and rest separately and combine the results. It
just doesn't seem right to use stateful devices in order to beat up
otherwise applicative mapcar into doing a job that can be done in a
straightforward way with applicative programming.

If you need this as a frequently used idiom in the program, you could
make your own mapcar-like function which has the flexibility to do
that.

  (defun mapcar-first-rest (first-func second-func &rest lists)
    (cons (apply first-func (mapcar #'first lists))
          (apply #'mapcar second-func (mapcar #'rest lists))))

Now you can do:

  ;; add 1 to first item, 2 to all the rest
  (mapcar-first-rest #'(lambda (n) (1+ n)) #'(lambda (n) (+ 2 n)) '(1
2 3))

Given an interface like this:

  (defun bar (item &optional treat-as-first) ...)

You can use a lambda adapter:

  (mapcar-first-rest #'(lambda (x) (bar x t)) #'bar list)

And if that's needed as a frequent idiom, of course it can be wrapped
with a function also. In the following example, the flag is the first
argument, because the function is expected to take as many additional
arguments as there are lists.

  (defun mapcar-flagged-first (function &rest lists)
    (cons (apply function t (mapcar #'first lists))
          (apply #'mapcar #'(lambda (&rest items) (apply function nil
items))
                          (mapcar #'rest lists))))

  ;; test
  (mapcar-flagged-first #'list '(1 2 3) '(4 5 6))

  -> ((T 1 4) (NIL 2 5) (NIL 3 6))