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?
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?
········@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
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
···@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
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.
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.
"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.
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
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
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
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
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
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
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/
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.
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/
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 :)
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
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)
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
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/
"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.
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.
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.
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...
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))