I'm writing an Emacs lisp function that uses two local variables.
The values of these local variables depend on the result of an
elaborate test condition. I couldn't figure anything better than
(let ((foo (if (<elaborate-condition>) 1 2)
(bar (if (<elaborate-condition>) 3 4))
(blah-blah-blah))
...except that "<elaborate-condition>" is much hairier-looking than it
looks above. This looks awful to me. I tried improving it with
(let* ((test (<elaborate-condition>))
(foo (if test 1 2))
(bar (if test 3 4)))
(blah-blah-blah))
...but it still seems to me very clumsy to have to check test twice.
What's the more clueful way to code this?
TIA!
jill
--
To s&e^n]d me m~a}i]l r%e*m?o\v[e bit from my a|d)d:r{e:s]s.
J Krugman <···········@yahbitoo.com> wrote:
+---------------
| I'm writing an Emacs lisp function that uses two local variables.
| The values of these local variables depend on the result of an
| elaborate test condition. I couldn't figure anything better than
|
| (let ((foo (if (<elaborate-condition>) 1 2)
| (bar (if (<elaborate-condition>) 3 4))
| (blah-blah-blah))
|
| ...except that "<elaborate-condition>" is much hairier-looking than it
| looks above. This looks awful to me. I tried improving it with
|
| (let* ((test (<elaborate-condition>))
| (foo (if test 1 2))
| (bar (if test 3 4)))
| (blah-blah-blah))
|
| ...but it still seems to me very clumsy to have to check test twice.
| What's the more clueful way to code this?
+---------------
Actually, I think your second version is fine [a check of a simple
variable is about the fastest test there is], but if it really
bothers you, you could always manually rewrite the LET body into
an equivalent local function, with conditional calls of it:
(flet ((body (foo bar)
...blah
blah
blah...))
(if <elaborate-condition>
(body 1 3)
(body 2 4)))
In your case, it may not be worth rewriting that way, but it can
be quite useful when there are more than two possible choices to
consider and/or more than two variables in the body. e.g.:
(flet ((body (foo bar baz gorp)
...blah
blah
blah...))
(cond
(<test-for-variant-1>
(body 1 3 'joe "green"))
(<test-for-variant-2>
(body 2 4 'phil "light blue"))
(<test-for-variant-3>
(body 3 7 'sally "very dark orange"))
(t
(body 17 9 'john-doe "while"))))
-Rob
-----
Rob Warnock <····@rpw3.org>
627 26th Avenue <URL:http://rpw3.org/>
San Mateo, CA 94403 (650)572-2607
+ J Krugman <···········@yahbitoo.com>:
| (let* ((test (<elaborate-condition>))
| (foo (if test 1 2))
| (bar (if test 3 4)))
| (blah-blah-blah))
|
| ...but it still seems to me very clumsy to have to check test twice.
Why does that seem clumsy to you? I think it looks perfectly fine.
But if you insist:
(let ((test (<elaborate-condition>)))
(multiple-value-bind (foo bar)
(if test
(values 1 2)
(values 3 4))
(blah-blah-blah)))
--
* Harald Hanche-Olsen <URL:http://www.math.ntnu.no/~hanche/>
- Debating gives most of us much more psychological satisfaction
than thinking does: but it deprives us of whatever chance there is
of getting closer to the truth. -- C.P. Snow
+ J Krugman <···········@yahbitoo.com>:
| (let* ((test (<elaborate-condition>))
| (foo (if test 1 2))
| (bar (if test 3 4)))
| (blah-blah-blah))
|
| ...but it still seems to me very clumsy to have to check test twice.
Why does that seem clumsy to you? I think it looks perfectly fine.
But if you insist:
(let ((test (<elaborate-condition>)))
(multiple-value-bind (foo bar)
(if test
(values 1 2)
(values 3 4))
(blah-blah-blah)))
--
* Harald Hanche-Olsen <URL:http://www.math.ntnu.no/~hanche/>
- Debating gives most of us much more psychological satisfaction
than thinking does: but it deprives us of whatever chance there is
of getting closer to the truth. -- C.P. Snow
In <···············@shuttle.math.ntnu.no> Harald Hanche-Olsen <······@math.ntnu.no> writes:
>+ J Krugman <···········@yahbitoo.com>:
>| (let* ((test (<elaborate-condition>))
>| (foo (if test 1 2))
>| (bar (if test 3 4)))
>| (blah-blah-blah))
>|
>| ...but it still seems to me very clumsy to have to check test twice.
>Why does that seem clumsy to you?
Because it's the equivalent of this in C:
if (test)
foo = 1;
else
foo = 2;
if (test)
bar = 3;
else
bar = 4;
...which IMO is clumsier than
if (test) {
foo = 1;
bar = 3;
}
else {
foo = 2;
bar = 4;
}
The "clumsy version" ignores C's ability to conditionally execute
more than one line of code. I shudder to think of a C program in
which every if statement could be followed by only one line of
code. How awful! The clumsiness is more apparent if instead of
having just 2 variables whose values depended on the value of test
I had 10 or 20 such variables.
Thank you all for the various suggestions. I liked the one about
turning the test into its own little function best of all, since
testing for "elaborate-condition" is something that I'll probably
need elsewhere.
jill
--
To s&e^n]d me m~a}i]l r%e*m?o\v[e bit from my a|d)d:r{e:s]s.
J Krugman <···········@yahbitoo.com> writes:
> In <···············@shuttle.math.ntnu.no> Harald Hanche-Olsen <······@math.ntnu.no> writes:
>
>>+ J Krugman <···········@yahbitoo.com>:
>
>>| (let* ((test (<elaborate-condition>))
>>| (foo (if test 1 2))
>>| (bar (if test 3 4)))
>>| (blah-blah-blah))
>>|
>>| ...but it still seems to me very clumsy to have to check test twice.
>
>>Why does that seem clumsy to you?
>
> Because it's the equivalent of this in C:
>
> if (test)
> foo = 1;
> else
> foo = 2;
>
> if (test)
> bar = 3;
> else
> bar = 4;
>
> ...which IMO is clumsier than
>
> if (test) {
> foo = 1;
> bar = 3;
> }
> else {
> foo = 2;
> bar = 4;
> }
>
> The "clumsy version" ignores C's ability to conditionally execute
> more than one line of code. I shudder to think of a C program in
> which every if statement could be followed by only one line of
> code. How awful! The clumsiness is more apparent if instead of
> having just 2 variables whose values depended on the value of test
> I had 10 or 20 such variables.
Well, the C version would actuall have to be:
int foo;
int bar;
if (elaborate_condition()) {
foo = 1;
bar = 3;
}
else {
foo = 2;
bar = 3;
}
Which you can translate into Common Lisp like this, if you must:
(let (foo bar)
(if (elaborate-condition)
(setf foo 1 bar 3)
(setf foo 2 bar 4))
...)
Though that's not so idomatic in Lisp.
-Peter
--
Peter Seibel ·····@javamonkey.com
Lisp is the red pill. -- John Fraser, comp.lang.lisp
"Peter Seibel" <·····@javamonkey.com> wrote in message
···················@javamonkey.com...
> (let (foo bar)
> (if (elaborate-condition)
> (setf foo 1 bar 3)
> (setf foo 2 bar 4))
> ...)
>
> Though that's not so idomatic in Lisp.
Hm. Looks fine to me. True it is unusual not to initialize in a let but I
wouldn't say unidiomatic....
--
Coby Beck
(remove #\Space "coby 101 @ big pond . com")
"J Krugman" <···········@yahbitoo.com> wrote in message
·················@reader2.panix.com...
> In <···············@shuttle.math.ntnu.no> Harald Hanche-Olsen
> <······@math.ntnu.no> writes:
>
>>+ J Krugman <···········@yahbitoo.com>:
>
>>| (let* ((test (<elaborate-condition>))
>>| (foo (if test 1 2))
>>| (bar (if test 3 4)))
>>| (blah-blah-blah))
>>|
>>| ...but it still seems to me very clumsy to have to check test twice.
>
>>Why does that seem clumsy to you?
>
> Because it's the equivalent of this in C:
...
> The "clumsy version" ignores C's ability to conditionally execute
> more than one line of code. I shudder to think of a C program in
> which every if statement could be followed by only one line of
To be fair in your C->Lisp comparison you need to include the lines that
declare foo and bar, ie you are comparing declaring and initializing with
just assigning.
Here is another way:
(let (foo bar (test (<elaborate-condition>)))
(if test
(setf foo 1 bar 3)
(setf foo 2 bar 4))
(blah-blah-blah))
or:
(let ((foo 1)
(bar 2)
(test (<elaborate-condition>)))
(unless test
(setf foo 2 bar 4))
(blah-blah-blah))
General point: If you have more than one line of code needed for the
branches of your if use progn
(if test
(progn
(blah)
(blah))
(progn
(not-blah)
(not-blah)))
I didn't need it for your snippet because setf can handle more than one
assignment at a time.
--
Coby Beck
(remove #\Space "coby 101 @ big pond . com")
J Krugman wrote:
I tried improving it with
(let* ((test (<elaborate-condition>))
(foo (if test 1 2))
(bar (if test 3 4)))
(blah-blah-blah))
...but it still seems to me very clumsy to have to
check test twice.
That looks fine to me. Indeed, my taste would be for the
even clumsier
(let ((test (elaborate-condition)))
(let ((foo (if test 1 2))
(bar (if test 3 4)))
(cons foo bar)))
because it speaks more directly of what I'm doing.
First I'm saving the result of a computation,
(elaborate-condition), in a temporary variable.
This suggests to the maintainer that I need the result of
the elaborate-condition in more than one place.
Second I use my saved result twice. Expectation fulfilled.
Alternatively you could pull out blah-blah-blah into a
function, like this:
(defun elaborate-condition ()
(print "too hard to compute, please type in by hand")
(read))
(defun blah (foo bar)
(print 'blah-blah-blah)
(cons foo bar))
(if (elaborate-condition)
(blah 1 3)
(blah 2 4))
This news group mostly talks about Common Lisp, which gives
you lots of rope to play with, for example:
(let (foo bar)
(setf (values foo bar)
(if (elaborate-condition)
(values 1 3)
(values 2 4)))
(print 'blah-blah-blah)
(cons foo bar))
or
(apply (lambda (foo bar)
(print 'blah-blah-blah)
(cons foo bar))
(if (elaborate-condition)
(list 1 3)
(list 2 4)))
and this second one seems also to work in emacs lisp, which
I've not studied.
Alan Crowe
Edinburgh
Scotland
On Sun, 13 Feb 2005 07:32:08 +0000, J Krugman wrote:
> (let* ((test (<elaborate-condition>))
> (foo (if test 1 2))
> (bar (if test 3 4)))
> (blah-blah-blah))
>
> ...but it still seems to me very clumsy to have to check test twice.
> What's the more clueful way to code this?
Perhaps:
(let ((foo 2) (bar 4))
(when (<elaborate-condition>)
(psetf foo 1 bar 3))
...)
Regards,
Adam
J Krugman wrote:
> I'm writing an Emacs lisp function that uses two local variables.
> The values of these local variables depend on the result of an
> elaborate test condition. I couldn't figure anything better than
>
> (let ((foo (if (<elaborate-condition>) 1 2)
> (bar (if (<elaborate-condition>) 3 4))
> (blah-blah-blah))
>
> ...except that "<elaborate-condition>" is much hairier-looking than it
> looks above. This looks awful to me. I tried improving it with
>
> (let* ((test (<elaborate-condition>))
> (foo (if test 1 2))
> (bar (if test 3 4)))
> (blah-blah-blah))
>
> ...but it still seems to me very clumsy to have to check test twice.
> What's the more clueful way to code this?
You might be interested in Michael Parker's WITH macro. If I
understand his documentation right, your example would become
(with
vars (foo bar) = (if (<elaborate-condition>) '(1 2) '(3 4))
do
(blah-blah-blah))
which is just syntactic sugar for the multiple-value-bind solution
mentioned previously.
Of course this is not a standard macro, so if you start using it other
people may get confused by your code.
It's at <http://www.geocities.com/mparker762/with.html>.
In article <··········@news1.newsguy.com>, Dan Schmidt <····@dfan.org>
wrote:
> > ...but it still seems to me very clumsy to have to check test twice.
> > What's the more clueful way to code this?
>
> You might be interested in Michael Parker's WITH macro. If I
> understand his documentation right, your example would become
>
> (with
> vars (foo bar) = (if (<elaborate-condition>) '(1 2) '(3 4))
> do
> (blah-blah-blah))
yep, that's pretty much it.
Mostly, WITH keeps your code from shooting off to the right if you're
nesting binding forms. It also maintains the variables closer to the
left where they're easier to pick up visually. And it makes using
types declarations simpler, plus you can put in additional assertion
checking.
But it's not very lisp-like, for which it has garnered some criticism
by the sort of folks that disdain syntactic sugar.
J Krugman wrote:
> I'm writing an Emacs lisp function that uses two local variables.
> The values of these local variables depend on the result of an
> elaborate test condition. I couldn't figure anything better than
>
> (let ((foo (if (<elaborate-condition>) 1 2)
> (bar (if (<elaborate-condition>) 3 4))
> (blah-blah-blah))
>
Well, one more way, though I do not know if Elisp has
&aux vars.
(defun blah-blah-blah (elaborate-condition
&aux
(foo (if elaborate-condition 1 2))
(bar (if elaborate-condition 3 4)))
(more-blah-blah-blah))
then do blah-blah-blah with
(blah-blah-blah (<elaborate-condition>))
blah-blah-blah...
Or a create a macro,
(defmacro conditional-let (condition vars &body body)
(let ((condition-result (gensym)))
`(let* ((,condition-result ,condition)
,@(mapcar (lambda (var) `(,(car var) (if ,condition-result ,@(rest var))))
vars))
,@body)))
CL-USER 4 > (conditional-let (elaborate-condition)
((foo 1 2) (bar 3 4))
(blah-blah-blah))
Wade
+ Wade Humeniuk <··················@telus.net>:
| J Krugman wrote:
| > I'm writing an Emacs lisp function that uses two local variables.
^^^^^
Whoops, I didn't notice that the first time around.
Scratch my multiple-value-bind solution, then.
Oh, actually it should work with (require 'cl), though it fakes
multiple values by using lists. I notice that even flet and labels
are supported, though I never used them in elisp. And they create
dynamic bindings, not lexical ones.
(We should have warned the OP to go to an emacs group, perhaps.)
| (defmacro conditional-let (condition vars &body body)
| (let ((condition-result (gensym)))
| `(let* ((,condition-result ,condition)
| ,@(mapcar (lambda (var) `(,(car var) (if ,condition-result ,@(rest var))))
| vars))
| ,@body)))
Better call that conditional-let*, lest users be confused.
Or use the solution (found elsewhere in the thread) with an extra
layer of let, the outer one binding ,condition-result and the inner
one the variables.
--
* Harald Hanche-Olsen <URL:http://www.math.ntnu.no/~hanche/>
- Debating gives most of us much more psychological satisfaction
than thinking does: but it deprives us of whatever chance there is
of getting closer to the truth. -- C.P. Snow
Harald Hanche-Olsen <······@math.ntnu.no> writes:
> + Wade Humeniuk <··················@telus.net>:
>
> | J Krugman wrote:
> | > I'm writing an Emacs lisp function that uses two local variables.
> ^^^^^
>
> Whoops, I didn't notice that the first time around.
> Scratch my multiple-value-bind solution, then.
> Oh, actually it should work with (require 'cl), though it fakes
> multiple values by using lists. I notice that even flet and labels
> are supported, though I never used them in elisp. And they create
> dynamic bindings, not lexical ones.
Nope, it's more confusing than that. flet creates dynamic bindings,
labels creates lexical ones. *Neither* of them has CL:FLET's scoping,
they both make the bindings visible in the bodies of all the
functions, like CL:LABELS. It makes a certian amount of sense in the
context of Emacs, but as part of the cl package, they really should
have chosen another naming scheme.
(FWIW, I don't think you can get CL:FLET-like semantics in Emacs Lisp,
for that you really need the ability to bind the function slot of a
symbol, not just set it. I'd be very interested if someone could
prove me wrong, though :)
···@conquest.OCF.Berkeley.EDU (Thomas F. Burdick) writes:
> (FWIW, I don't think you can get CL:FLET-like semantics in Emacs Lisp,
> for that you really need the ability to bind the function slot of a
> symbol, not just set it. I'd be very interested if someone could
> prove me wrong, though :)
How about:
(defmacro xflet (defs &rest body)
(let ((tmps (mapcar #'gensym defs)))
`(let ,(loop for tmp in tmps for (_ . rest) in defs
collect `(,tmp (lambda ,@rest)))
(labels ,(loop for tmp in tmps for (n args _) in defs
collect `(,n ,args (funcall ,tmp ,@args)))
,@body))))
(defun foo ()
(xflet ((a () (b))
(b () 'b))
(a)))
(foo) ==> Symbol's function definition is void: b
Helmut Eller <········@stud3.tuwien.ac.at> writes:
> ···@conquest.OCF.Berkeley.EDU (Thomas F. Burdick) writes:
>
> > (FWIW, I don't think you can get CL:FLET-like semantics in Emacs Lisp,
> > for that you really need the ability to bind the function slot of a
> > symbol, not just set it. I'd be very interested if someone could
> > prove me wrong, though :)
>
> How about:
>
> (defmacro xflet (defs &rest body)
> (let ((tmps (mapcar #'gensym defs)))
> `(let ,(loop for tmp in tmps for (_ . rest) in defs
> collect `(,tmp (lambda ,@rest)))
> (labels ,(loop for tmp in tmps for (n args _) in defs
> collect `(,n ,args (funcall ,tmp ,@args)))
> ,@body))))
>
> (defun foo ()
> (xflet ((a () (b))
> (b () 'b))
> (a)))
>
> (foo) ==> Symbol's function definition is void: b
Hey, you're right, that does work. Actually seems pretty obvious in
retrospect, thanks!