Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

IF & AND redefined, why?

15 views
Skip to first unread message

Jeff Sandys

unread,
Apr 17, 2001, 6:41:50 PM4/17/01
to
While working on some code, I found AND and IF redefined.
I can't figure out why the programmer did it this way.

(defmacro myif (mytest mythen &optional myelse)
`(let ((it ,mytest))
(if it ,mythen ,myelse)))

(defmacro myand (&rest args)
(cond ((null args) t)
((null (cdr args)) (car args))
(t `(myif ,(car args) (myand ,@(cdr args))))))

Can anyone explain the difference between these and the
regular AND and IF? Would there be any difference in
behavior if I substituted the regular AND and IF?

Thanks,
Jeff Sandys

Johann Hibschman

unread,
Apr 17, 2001, 7:23:52 PM4/17/01
to
Jeff Sandys writes:

> Can anyone explain the difference between these and the
> regular AND and IF? Would there be any difference in
> behavior if I substituted the regular AND and IF?

Those versions bind the variable "it" to the result of the test.
If the code uses that variable, then, yes, you will have a problem
with it working.

An example of why the programmer probably wanted to do this would be

(myif (gethash h :name) ; binds it = (gethash h :name)
(format t "~A" it) ; uses the value of it
(format t "Key :name not found."))

A similar thing applies to the and variant.

--Johann

--
Johann Hibschman joh...@physics.berkeley.edu

Johann Hibschman

unread,
Apr 17, 2001, 7:28:08 PM4/17/01
to
Johann Hibschman writes:

> (myif (gethash h :name) ; binds it = (gethash h :name)
> (format t "~A" it) ; uses the value of it
> (format t "Key :name not found."))

Oops. I have Scheme on my brain. I'm sure you know what I meant,
even if this is broken. No need to take out the thumbscrews.

Ted Sandler

unread,
Apr 18, 2001, 12:30:33 AM4/18/01
to

Jeff Sandys wrote:
>
> While working on some code, I found AND and IF redefined.
> I can't figure out why the programmer did it this way.
>
> (defmacro myif (mytest mythen &optional myelse)
> `(let ((it ,mytest))
> (if it ,mythen ,myelse)))

The author of this code is trying to use variable capture to avoid
recomputing the conditional clause of the "if statement". Such a
technique could be useful if the result of the conditional clause was
computationally expensive and having to recompute it resulted in
inefficient code.

e.g. the following code has to compute the function "calculate-answer"
twice:

(if (calculate-answer)
(format t "The answer is ~A" (calculate-answer))
(format t "There is no answer"))

The "myif" macro sought to avoid this by setting "it" to the result of
the conditional as in:

(myif (calculate-answer)
(format t "The answer is ~A" it)
(format t "There is no answer"))

Quite frankly, I think defining such a macro only serves to confuse
people. Having to type out an extra "let" statement isn't going give
your friend RSI and it will give everyone the ablility to read and
understand the code clearly.


> (defmacro myand (&rest args)
> (cond ((null args) t)
> ((null (cdr args)) (car args))
> (t `(myif ,(car args) (myand ,@(cdr args))))))

As for this "myand", I have no clue what's going on here...


> Can anyone explain the difference between these and the
> regular AND and IF? Would there be any difference in
> behavior if I substituted the regular AND and IF?

Yes there's a difference, and unless you have good reason, avoid "myif"
and "myand" like the plague.

--
tedsa...@att.net

Christopher Stacy

unread,
Apr 18, 2001, 12:27:35 AM4/18/01
to
The programmer is binding a variable called IT to value of the test form,
and that variable is visible to the two then/else consequent clauses.
You would need to examine all the callers of the macro to see if the
variable IT is being used anywhere, and bind the variable for them.

If the program in question is supposed to be anything like normal
Lisp code, I would recommend doing that and eliminating the weird
redefinitions of the normal IF and AND.

Dr. Edmund Weitz

unread,
Apr 18, 2001, 1:51:40 AM4/18/01
to
I just wanted to add that the technique used here is what Paul Graham
(in 'On Lisp') calls 'anaphoric macros'. If my memory serves me right,
there's a whole chapter about this technique.

So, I think the guy who wrote the orginal code has been bashed enough.
I think he new what he did and he was just doing something that (under
certain circumstances, of course) is recommended in a book that almost
everybody seems to love.

Cheers,
Edi.

--
Dr. Edmund Weitz
Hamburg
Germany
http://www.weitz.de/


Francis Leboutte

unread,
Apr 18, 2001, 6:35:02 AM4/18/01
to
Ted Sandler <tedsa...@worldnet.att.net> wrote:

>... Having to type out an extra "let" statement isn't going give


>your friend RSI and it will give everyone the ablility to read and
>understand the code clearly.

I often use an if-bind macro ,for example :

(if-bind (o (part-of node)) o node)

(defmacro if-bind ((var form) form1 form2)
`(let ((,var ,form))
(if ,var
,form1
,form2)))


--
Francis Leboutte www.algo.be +32-(0)4.388.39.19

Erik Naggum

unread,
Apr 18, 2001, 7:11:23 AM4/18/01
to
* Ted Sandler <tedsa...@worldnet.att.net>

> e.g. the following code has to compute the function "calculate-answer"
> twice:
>
> (if (calculate-answer)
> (format t "The answer is ~A" (calculate-answer))
> (format t "There is no answer"))
>
> The "myif" macro sought to avoid this by setting "it" to the result of
> the conditional as in:
>
> (myif (calculate-answer)
> (format t "The answer is ~A" it)
> (format t "There is no answer"))

Programmers with slightly more experience would probably write this:

(format t "~:[There is no answer~;The answer is ~:*~A~]" (calculate-answer))

Similar options are frequently available.

Incidentally, you could name your anaphoric variable $_ or whatever Perl
uses for the last value computed, and avoid the problem that it might
look as if it were a tasteful thing to do. "it" is not a reserved word,
but it becomes one in myif and myand, and that's even worse than using a
disgusting-looking Perlism. If you really, really want anaphoric if and
and, at least allow the programmer to choose the variable name, as in

(iflet ((with-it (calculate-answer))
(whatever with-it)
(whatever-else)))

Note: this is intentionally syntactically like a binding, so as not to
confuse readers to believe that with-it is a function call. This also
makes it possible to roll this into a normal if. (Provided precautions
are taken against misinterpreting lamda forms, which are syntactically
different from the binding form even in the one really pathological case.)

And don't forget the functional style:

((lambda (x) (if x (whatever x) (whatever-else))) (calculate-answer))

#:Erik
--
I found no peace in solitude.
I found no chaos in catastrophe.
-- :wumpscut:

dave linenberg

unread,
Apr 18, 2001, 10:06:37 AM4/18/01
to
>
> * Newsgroups: comp.lang.lisp

> * From: Francis Leboutte <f.leb...@algo.be> wrote:
>
> I often use an if-bind macro ,for example :
>
> (if-bind (o (part-of node)) o node)
>
> (defmacro if-bind ((var form) form1 form2)
> `(let ((,var ,form))
> (if ,var
> ,form1
> ,form2)))

This works for single bindings, but what about for multiple bindings?
Inspired by Graham:

(defmacro when-bind-graham((var expr) &body body)
`(let ((,var ,expr))
(when ,var
,@body)))


I came up with the following (which I am sure can be critiqued as I consider myself a lisp newbie...) for parallel assignments,

(defmacro when-bind (lst &rest body)
"when-bind binds (multiple) non-nil expressions to
parallel inititiated variables.
If the expression is nil, when-bind returns nil.
Produces code in the following template
(when-bind ((var1 expr1) (var2 expr2) ...(varN exprN))
(form1)
(form2)
(formN)) is transformed to -->

(let ((var1 expr1)
(var2 expr2)
(varN exprN))
(when (and var1 var2 ... varN)
(form1)
(form2)
(formN))) "

(let* ((expr (mapcar #'car lst))
(code `(LET ,lst (WHEN (AND ,@expr)))))
(setf (third code)
(nconc (third code) body))
code))

And the following , when-bind*, for sequential assignments:

(defun let-when-template (lst)
"helper function for when-bind* macro"
(destructuring-bind (var expr) lst
`(let ((,var ,expr))
(when ,var))))

(defmacro when-bind* (lst &rest body)
"when-bind* binds (multiple) non-nil expressions to
sequentially inititiated variables.
If the expression is nil, when-bind* returns nil.
Produces code in the following template
(when-bind ((var1 expr1) (var2 expr2) ...(var-x expr-x))
(form1)
(form2)
.
.
(form-n)) is transformed to -->

(let ((var1 expr1))
(when var1
(let ((var2 expr2))
(when var2
.
.
(let ((var-x expr-x))
(when var-x
(form1)
(form2)
.
.
(form-n)))))))"
(let ((code (mapcar 'let-when-template lst)))
(loop for el1 in code
for el2 in (cdr code)
do (setf (third el1) (nconc (third el1) (list el2)))
finally (setf (third el2) (nconc (third el2) body)))
(car code)))

--
Posted from [199.35.146.133]
via Mailgate.ORG Server - http://www.Mailgate.ORG

Erik Naggum

unread,
Apr 18, 2001, 7:04:41 PM4/18/01
to
* Dr. Edmund Weitz

> I just wanted to add that the technique used here is what Paul Graham
> (in 'On Lisp') calls 'anaphoric macros'. If my memory serves me right,
> there's a whole chapter about this technique.

Let us improve on it, not accept it as gospel. I suggest, for instance,
that we expose the bindings of the anaphoric variables, instead of using
a "magic" variable whose binding is neither visible not changeable.

> So, I think the guy who wrote the orginal code has been bashed enough.
> I think he new what he did and he was just doing something that (under
> certain circumstances, of course) is recommended in a book that almost
> everybody seems to love.

It is one of few books on Common Lisp, it is fairly recent, and it has a
lot of merit to it, but I am not sure it is a "here are the facts" kind
of book, nor "this is how you do it", but rather "this is what I do, and
I think it's great". Mistaking it for any of the other types of book is
going to hurt people. I highly value Paul Graham's insights and opinions
and wouldn't want to be without them, _but_ I also think any reader of
such a book should be a good student and question his master. Go not
where he went, find out why he went where he did. Chances are the
destination is an accident of circumstances, but his choices were not.

Stig Hemmer

unread,
Apr 18, 2001, 1:19:57 PM4/18/01
to
e...@agharta.de (Dr. Edmund Weitz) writes:
> I just wanted to add that the technique used here is what Paul Graham
> (in 'On Lisp') calls 'anaphoric macros'. If my memory serves me right,
> there's a whole chapter about this technique.
>
> So, I think the guy who wrote the orginal code has been bashed enough.
> I think he new what he did and he was just doing something that (under
> certain circumstances, of course) is recommended in a book that almost
> everybody seems to love.

"On Lisp" tells the reader an aweful lot about what you _can_ do with
macros, but is rather silent on what you _should_ do with macros.

I loved the book for its sheer "Wow!" power as it made me reallize how
much power there is in Lisp-style macros. Some of his macros gives me
a trill. It is like a power tool, just begging to be used.

On the other hand, the code in the book is _not_ what I would call
good style. It is downright obfuscated at times.

For good style I would recommend Peter Norvigs book, "Paradigms of AI
Programming: Case Studies in Common Lisp". In this book Norvig
tackles some serious problems and shows you how to solve them. And
the code he writes is just beautiful. Very understandable. Nobody
should be afraid that this book is too hard for them, reading it will
be good for your soul, even if you don't understand everything. :-)

Stig Hemmer,
Jack of a Few Trades.

Harald Hanche-Olsen

unread,
Apr 18, 2001, 5:08:01 PM4/18/01
to
+ Erik Naggum <er...@naggum.net>:

| Programmers with slightly more experience would probably write this:
|
| (format t "~:[There is no answer~;The answer is ~:*~A~]" (calculate-answer))

Whereas programmers with slightly less experience, such as myself,
would more likely prefer

(let ((answer (calculate-answer)))
(if answer
(format t "The answer is ~A" answer)
(format t "There is no answer")))

which is still an improvement over the original macro hack, and is
readable by mere mortals to boot. 8-)

(Rats. I posted this with the logic reversed, but caught the mistake
right away and superseded the article.)
--
* Harald Hanche-Olsen <URL:http://www.math.ntnu.no/~hanche/>
- Yes it works in practice - but does it work in theory?

Kent M Pitman

unread,
Apr 24, 2001, 10:35:17 AM4/24/01
to
Harald Hanche-Olsen <han...@math.ntnu.no> writes:

> + Erik Naggum <er...@naggum.net>:
>
> | Programmers with slightly more experience would probably write this:
> |
> | (format t "~:[There is no answer~;The answer is ~:*~A~]" (calculate-answer))

Stylistically, I prefer to write this:

(format t "~:[There is no answer~;~:*The answer is ~A~]" (calculate-answer))

so as to allow the arg backing up to be done at the first possible point in
case another ~A or ~S is introduced between the point of the ~; and the
point of the ~A, to avoid further confusion.

> Whereas programmers with slightly less experience, such as myself,
> would more likely prefer
>
> (let ((answer (calculate-answer)))
> (if answer
> (format t "The answer is ~A" answer)
> (format t "There is no answer")))
>
> which is still an improvement over the original macro hack, and is
> readable by mere mortals to boot. 8-)

Well, while a newbie migth indeed have a rpeferred learning order, I
recommend everyone at least use proper end of sentence punctuation
and proper newline breaking, which means getting ~% and ~& into use.
I recommend, for example,

(format t "~&The answer is ~A.~%" answer)
and
(format t "~&There is no answer.~%")

That is, always start a line of uncertain nature with a freshline, as a
guard against someone else having forgotten ~%, and always end with ~%
oout of politeness. Further, always end a sentence with a period out of
proper grammar.

Then again, I suggest "No answer was computed." rather than
"There is no answer.", as the latter sounds more like something you'd
get in response to (is-there-a-god?). [Philosophical point.]

Btw, I don't think there's any good reason for "mere mortals" not to
know about both ~@[...~], ~:[...~;...~], ~{...~}, and ~:{...~}. These
are far from obscure uses. So anyone who doesn't know these should
look into them at their earliest convenience.

And likewise the idiom ~:* is so often used that everyone should know it
as soon as possible.

Francis Leboutte

unread,
Apr 25, 2001, 5:30:59 AM4/25/01
to
dline...@nabny.com (dave linenberg) wrote:

>I came up with the following (which I am sure can be critiqued as I consider
>myself a lisp newbie...) for parallel assignments,
>
>(defmacro when-bind (lst &rest body)
> "when-bind binds (multiple) non-nil expressions to
> parallel inititiated variables.
> If the expression is nil, when-bind returns nil.
> Produces code in the following template
> (when-bind ((var1 expr1) (var2 expr2) ...(varN exprN))
> (form1)
> (form2)
> (formN)) is transformed to -->

> ...

maybe you are not going to use this often because it means the exprN expressions
are also evaluated for their side effects (they are always all evaluated even if
one of them is NIL).

>And the following , when-bind*, for sequential assignments:

>...


>(defmacro when-bind* (lst &rest body)
> "when-bind* binds (multiple) non-nil expressions to
> sequentially inititiated variables.
> If the expression is nil, when-bind* returns nil.
> Produces code in the following template
> (when-bind ((var1 expr1) (var2 expr2) ...(var-x expr-x))
> (form1)
> (form2)

> (form-n)) is transformed to -->

> ...

This one should be useful. As soon as an exprN expression is NIL, the macro
returns NIL.

The when-bind (or if-bind) I use is bit more elaborated that the one I post some
days ago; it allows me to write :

(let ((val -1))
(when-bind (v val #'integerp #'oddp #'minusp)
(format nil "~D" val)))
=> "-1"

(let ((val 1))
(when-bind (v val #'integerp #'oddp #'minusp)
(format nil "~D" val)))
=> NIL

;;; function intersection. See On Lisp
(defun fint (fn &rest fns)
(if (null fns)
fn
(let ((chain (apply #'fint fns)))
(lambda (x)
(and (funcall fn x) (funcall chain x))))))


(defmacro when-bind ((var form &rest predicates) &body body)
`(let ((,var ,form))
,(if (consp predicates)
`(if (and ,var (funcall (fint ,@predicates) ,var))
(progn ,@body)
NIL)
`(if ,var
(progn ,@body)
NIL))))
--
www.algo.be
Logo programming : www.algo.be/logo.html

0 new messages