Exercise 14.4 in Touretzky's book asks the student to write a macro
called SIMPLE-ROTATEF that switches the value of two variables.
The solution is easy:
(defmacro simple-rotatef (a b)
`(let ((temp ,a))
(setq ,a ,b)
(setq ,b temp)))
However, when I run it with OpenMCL, the macro works, but I get the
following:
;Compiler warnings :
; Undeclared free variable A (2 references), in an anonymous lambda
form.
; Undeclared free variable B (2 references), in an anonymous lambda
form.
How do I fix this? The answer in the book has the same problem.
Bob Felts wrote:
> Exercise 14.4 in Touretzky's book asks the student to write a macro
> called SIMPLE-ROTATEF that switches the value of two variables.
(psetf a b b a)
> The solution is easy:
>
> (defmacro simple-rotatef (a b)
> `(let ((temp ,a))
> (setq ,a ,b)
> (setq ,b temp)))
Bzzt! Parasitic capture!
(macroexpand '(simple-rotatef temp tamp))
--> (LET ((TEMP TEMP)) (SETQ TEMP TAMP) (SETQ TAMP TEMP))
> However, when I run it with OpenMCL, the macro works, but I get the
> following:
>
> ;Compiler warnings :
> ; Undeclared free variable A (2 references), in an anonymous lambda
> form.
This can't be referring to your macro's lambda parameter A.
Do you have a test case for your macro which uses A and B as parameters
for calling the macro, but does not define the variables A and B? I.e.
do you have the top level orm
(simple-rotatef a b)
somewhere without a (defvar a ...) and (defvar b ...) above it?
Kaz Kylheku <········@gmail.com> wrote:
> Bob Felts wrote:
> > Exercise 14.4 in Touretzky's book asks the student to write a macro
> > called SIMPLE-ROTATEF that switches the value of two variables.
>
> (psetf a b b a)
>
> > The solution is easy:
> >
> > (defmacro simple-rotatef (a b)
> > `(let ((temp ,a))
> > (setq ,a ,b)
> > (setq ,b temp)))
>
> Bzzt! Parasitic capture!
>
> (macroexpand '(simple-rotatef temp tamp))
>
> --> (LET ((TEMP TEMP)) (SETQ TEMP TAMP) (SETQ TAMP TEMP))
>
Well, yes. This is an introductory example of macros and things like
(gensym) haven't yet been covered.
> > However, when I run it with OpenMCL, the macro works, but I get the
> > following:
> >
> > ;Compiler warnings :
> > ; Undeclared free variable A (2 references), in an anonymous lambda
> > form.
>
> This can't be referring to your macro's lambda parameter A.
>
> Do you have a test case for your macro which uses A and B as parameters
> for calling the macro, but does not define the variables A and B? I.e.
> do you have the top level orm
>
> (simple-rotatef a b)
>
> somewhere without a (defvar a ...) and (defvar b ...) above it?
Yes. If I used defvar then everything worked. I'm curious as to why it
"fails" when I define a symbol via (setf foo something) (setf bar
something-else) (simple-rotatef foo bar). The interpreter knows foo and
bar exist without a defvar.
Bob Felts wrote:
>
> Yes. If I used defvar then everything worked. I'm curious as to why it
> "fails" when I define a symbol via (setf foo something) (setf bar
> something-else) (simple-rotatef foo bar). The interpreter knows foo and
> bar exist without a defvar.
>
Some of this stuff is confusing, so let's take it one step at a time.
First, you understand now that there is nothing wrong with your macro.
It's the "variables" you applied it to that are causing the problem.
It's just a coincidence that the parameters of your macro are A and B
and you used it with top-level "variables" A and B. Had you done:
(setq x 1 y 2)
(simple-rotatef x y)
You would have gotten similar warnings. By contrast, when you do:
(defvar a 8)
(defvar b 9)
(simple-rotatef a b)
You get no warnings.
Before we look at the real issue I wanted to touch on another problem.
You mention above "defining a symbol" via SETF. This is a little sloppy.
Symbols are typically created by the Lisp reader. Try this:
* (find-symbol "QWERTY")
NIL
NIL
* 'qwerty
QWERTY
* (find-symbol "QWERTY")
QWERTY
:INTERNAL
The very act of typing the symbol's name at the REPL causes the reader
to create the symbol. A symbol is a data structure with several "cells".
When you type 'x, the value is the symbol X, which appears to just be a
character string, i.e., the name of the symbol. However, this is merely
the printed representation:
* (type-of 'x)
SYMBOL
We typically use symbols to name variables (although variables need not
have names--generalized variables). But in order for Lisp to understand
what a name such as X "means" we have to associate the name with a
variable. This is called a binding. A binding connects a name (symbol)
with some storage in memory (a variable).
In Common Lisp we create bindings for lexical variables via LET or
function calls (LAMBDA binding). We use DEFVAR and DEFPARAMETER to bind
(global) special variables. There are no global lexical variables in
Common Lisp, and you did not use DEFVAR in your first experiments, so
technically you have not established a binding between your variable
names A and B and any variables. By simply using SETQ you have jumped
the gun. You can't assign a new value to a variable named A without
letting Lisp know what variable A is supposed to refer to. You may have
read in one of a number of well known Lisp books that you can "create"
variables via SETQ but it's "bad style"--you should use DEFVAR
(Touretzky, Graham, and Slade all make such claims). To make matters
worse, many examples in the ANSI standard appear to do just this. For
example:
http://www.lispworks.com/documentation/HyperSpec/Body/s_setq.htm
But as you can see this doesn't really make sense, and the behavior as
Pascal pointed out is unspecified.
You may not be up to it just yet, but someday you'll just have to sit
down and read section 3.1 of the HyperSpec:
http://www.lispworks.com/documentation/HyperSpec/Body/03_a.htm
This may be helpful too:
http://www.lispworks.com/documentation/HyperSpec/Body/t_symbol.htm
Aloha,
David Sletten
····@stablecross.com (Bob Felts) writes:
> Exercise 14.4 in Touretzky's book asks the student to write a macro
> called SIMPLE-ROTATEF that switches the value of two variables.
>
> The solution is easy:
>
> (defmacro simple-rotatef (a b)
> `(let ((temp ,a))
> (setq ,a ,b)
> (setq ,b temp)))
>
> However, when I run it with OpenMCL, the macro works, but I get the
> following:
>
> ;Compiler warnings :
> ; Undeclared free variable A (2 references), in an anonymous lambda
> form.
> ; Undeclared free variable B (2 references), in an anonymous lambda
> form.
>
> How do I fix this? The answer in the book has the same problem.
The problem is not IN the macro!
[124]> (defmacro simple-rotatef (a b)
`(let ((temp ,a))
(setq ,a ,b)
(setq ,b temp)))
SIMPLE-ROTATEF
[125]> (defvar a 1)
A
[126]> (defvar b 2)
B
[127]> (simple-rotatef a b)
1
[128]> (list a b)
(2 1)
[129]>
As for your macro, in Common Lisp, there's a PSETQ:
(defmacro simple-rotatef (a b)
`(psetq ,a ,b
,b ,a))
--
__Pascal Bourguignon__ http://www.informatimago.com/
-----BEGIN GEEK CODE BLOCK-----
Version: 3.12
GCS d? s++:++ a+ C+++ UL++++ P--- L+++ E+++ W++ N+++ o-- K- w---
O- M++ V PS PE++ Y++ PGP t+ 5+ X++ R !tv b+++ DI++++ D++
G e+++ h+ r-- z?
------END GEEK CODE BLOCK------
Pascal Bourguignon <······@informatimago.com> wrote:
> ····@stablecross.com (Bob Felts) writes:
>
> > Exercise 14.4 in Touretzky's book asks the student to write a macro
> > called SIMPLE-ROTATEF that switches the value of two variables.
> >
> > The solution is easy:
> >
> > (defmacro simple-rotatef (a b)
> > `(let ((temp ,a))
> > (setq ,a ,b)
> > (setq ,b temp)))
> >
> > However, when I run it with OpenMCL, the macro works, but I get the
> > following:
> >
> > ;Compiler warnings :
> > ; Undeclared free variable A (2 references), in an anonymous lambda
> > form.
> > ; Undeclared free variable B (2 references), in an anonymous lambda
> > form.
> >
> > How do I fix this? The answer in the book has the same problem.
>
> The problem is not IN the macro!
>
> [124]> (defmacro simple-rotatef (a b)
> `(let ((temp ,a))
> (setq ,a ,b)
> (setq ,b temp)))
> SIMPLE-ROTATEF
> [125]> (defvar a 1)
> A
> [126]> (defvar b 2)
> B
> [127]> (simple-rotatef a b)
> 1
> [128]> (list a b)
> (2 1)
> [129]>
>
>
I should have mentioned that I don't get the compiler warning if
variables are declared with defvar. But if you do:
(setf c '(c c c))
(setf d '(d d d))
(simple-rotatef c d)
then you get the warnings and I'd like to understand why.
····@stablecross.com (Bob Felts) writes:
> I should have mentioned that I don't get the compiler warning if
> variables are declared with defvar. But if you do:
>
> (setf c '(c c c))
> (setf d '(d d d))
> (simple-rotatef c d)
>
> then you get the warnings and I'd like to understand why.
Because using undeclared variables is specified as yielding undefined
(i.e., implementation defined) results.
/Jon
--
'j' - a n t h o n y at romeo/charley/november com
····@stablecross.com (Bob Felts) writes:
> I should have mentioned that I don't get the compiler warning if
> variables are declared with defvar. But if you do:
>
> (setf c '(c c c))
> (setf d '(d d d))
> (simple-rotatef c d)
>
> then you get the warnings and I'd like to understand why.
Because that's the way it is: dynamic variables must be defined with
DEFVAR or DEFPARAMETER. If you use SETF or SETQ or similar to assign
to dynamic variables that haven't been declared, then the behavior is
unspecified, and anything can happen, like the end of the universe(*),
or, if you're lucky, and error message.
If you don't want to declare dynamic variables, you can use lexical variables:
(let ((c '(c c c))
(d '(d d d)))
(simple-rotatef c d)
(print c)
(print d))
Note that there's this convention of naming dynamic variables with stars:
(defparameter *a* '(a a a)) ; I should have written
(defparameter *b* '(b b b))
(simple-rotatef *a* *b*)
(Note that defvar doesn't re-assign the variable if it already exist,
you must use defparameter for that.)
(*) Yes, Common Lisp is THAT powerfull! ;-)
--
__Pascal Bourguignon__ http://www.informatimago.com/
"A TRUE Klingon warrior does not comment his code!"
Bob Felts wrote:
> Exercise 14.4 in Touretzky's book asks the student to write a macro
> called SIMPLE-ROTATEF that switches the value of two variables.
>
> The solution is easy:
>
> (defmacro simple-rotatef (a b)
> `(let ((temp ,a))
> (setq ,a ,b)
> (setq ,b temp)))
>
If you use setf instead of setq, you can also use your mac like this:
(setf list '(a b c d))
(simple-rotatef (first list) (third list))
--> '(c b a d)
Also, your macro does nonsense if one of your vars has the name temp.
So it would be cleaner to gensym on expansion time:
(defmacro less-simple-rotatef (a b)
(let ((temp (gensym)))
`(let ((,temp ,a))
(setf ,a ,b)
(setf ,b temp))))
But of course this can be avoided using the simple psetf.
At first, I thought a and b get evaluated twice here if they are
non-trivial places (or symbol-macros) and that this might cause the
warning. But that seems incorrect as one of these times, it will
actually be some function named (setf ..) which is called, so I guess
there can't be "less evaluation", right?
Or am I overlooking some crazy advanced-setf-stuff that could be used here?
Benjamin Teuber <······@web.de> writes:
> At first, I thought a and b get evaluated twice here if they are
> non-trivial places (or symbol-macros) and that this might cause the
> warning. But that seems incorrect as one of these times, it will
> actually be some function named (setf ..) which is called, so I guess
> there can't be "less evaluation", right?
> Or am I overlooking some crazy advanced-setf-stuff that could be used here?
You're overlooking this:
(defmacro less-simple-rotatef (a b)
(let ((temp (gensym)))
`(let ((,temp ,a))
(setf ,a ,b)
(setf ,b ,temp))))
(defparameter *a* (make-array 10 :initial-contents '(1 2 3 4 5 6 7 8 9 0)))
(defparameter *b* -1)
(defparameter *i* 0)
[146]> (LESS-SIMPLE-ROTATEF (aref *a* (incf *i*)) *b*)
2
[148]> (values *a* *b* *i*)
#(1 2 -1 4 5 6 7 8 9 0) ;
2 ;
2
Instead of: #(1 -1 3 4 5 6 7 8 9 0) ;
As you can see, you're incrementing twice *i*:
[150]> (setf *print-circle* nil)
NIL
[151]> (macroexpand-1 '(LESS-SIMPLE-ROTATEF (aref *a* (incf *i*)) *b*))
(LET ((#:G6377 (AREF *A* (INCF *I*))))
(SETF (AREF *A* (INCF *I*)) *B*)
(SETF *B* #:G6377)) ;
T
The correct way is indeed to use setf-expanders.
--
__Pascal Bourguignon__ http://www.informatimago.com/
"A TRUE Klingon warrior does not comment his code!"
Benjamin Teuber <······@web.de> writes:
> Pascal Bourguignon wrote:
>> You're overlooking this:
>> (defmacro less-simple-rotatef (a b)
>> (let ((temp (gensym)))
>> `(let ((,temp ,a))
>> (setf ,a ,b)
>> (setf ,b ,temp))))
>> (defparameter *a* (make-array 10 :initial-contents '(1 2 3 4 5 6 7 8
>> 9 0)))
>> (defparameter *b* -1)
>> (defparameter *i* 0)
>> [146]> (LESS-SIMPLE-ROTATEF (aref *a* (incf *i*)) *b*)
>> 2
>> [148]> (values *a* *b* *i*)
>> #(1 2 -1 4 5 6 7 8 9 0) ;
>
> Ah, thank you.
>
>> The correct way is indeed to use setf-expanders.
>
> Could you (or someone else) give some code example how to do it correct?
I've got this, to be able to write: (setf (nthcdr index list) new)
(define-setf-expander nthcdr (n place &environment env)
;; Kalle Olavi Niemitalo <···@iki.fi>
(multiple-value-bind (vars vals store-vars writer-form reader-form)
(get-setf-expansion place env)
(let* ((gn (gensym))
(gstore (if store-vars (first store-vars) (gensym "STORE"))))
(values (list* gn vars)
(list* n vals)
(list gstore)
;; Intentionally leaves 0.0 unrecognized.
`(if (eql ,gn 0)
,(case (length store-vars)
(0 `(progn ,writer-form ,gstore))
(1 writer-form)
(t `(let ,(rest store-vars)
(values ,writer-form))))
(setf (cdr (nthcdr (1- ,gn) ,reader-form)) ,gstore))
`(nthcdr ,gn ,reader-form)))))
;; To handle (incf (nthcdr 0 foo)), you need
;; DEFINE-SETF-EXPANDER rather than DEFSETF. Implementations
;; have been posted at least twice in this newsgroup:
;;
;; 1998-10-21 <····················@engc.bu.edu>
;; 2004-10-30 <···················@Astalo.kon.iki.fi>
;;
;; To satisfy point 13 of CLHS section 11.1.2.1.2 (assuming CLiki
;; issue DEFINE-SETF-METHOD:FIX), you'd also have to shadow NTHCDR.
--
__Pascal Bourguignon__ http://www.informatimago.com/
"Do not adjust your mind, there is a fault in reality"
-- on a wall many years ago in Oxford.