From: J Krugman
Subject: Noob: conditional let?
Date: 
Message-ID: <cumvpo$f51$1@reader2.panix.com>
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.

From: Rob Warnock
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <luednTh9tJtzgpLfRVn-hg@speakeasy.net>
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
From: Harald Hanche-Olsen
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <pcowttclruj.fsf@shuttle.math.ntnu.no>
+ 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
From: Harald Hanche-Olsen
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <pcosm40lrli.fsf@shuttle.math.ntnu.no>
+ 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
From: J Krugman
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <cunv0i$adc$1@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:

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.
From: Peter Seibel
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <m3650wcody.fsf@javamonkey.com>
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
From: Coby Beck
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <8SwQd.52617$gA4.26367@edtnps89>
"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")
From: Coby Beck
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <5WNPd.28835$K54.1750@edtnps84>
"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")
From: Alan Crowe
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <86mzu8ogyg.fsf@cawtech.freeserve.co.uk>
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
From: Adam Warner
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <pan.2005.02.13.12.28.52.873267@consulting.net.nz>
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
From: Dan Schmidt
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <cuqfuu0glp@news1.newsguy.com>
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>.
From: Michael Parker
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <140220051959513794%michaelparker@earthlink.net>
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.
From: Wade Humeniuk
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <8l4Qd.46903$gA4.14487@edtnps89>
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
From: Harald Hanche-Olsen
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <pcobranyqp7.fsf@shuttle.math.ntnu.no>
+ 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
From: Thomas F. Burdick
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <xcvvf8th7dq.fsf@conquest.OCF.Berkeley.EDU>
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 :)
From: Helmut Eller
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <m2is4tlcfe.fsf@stud3.tuwien.ac.at>
···@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
From: Thomas F. Burdick
Subject: Re: Noob: conditional let?
Date: 
Message-ID: <xcvsm3vgd48.fsf@conquest.OCF.Berkeley.EDU>
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!