From: Christopher Dollin
Subject: CL question - conditional list elements
Date: 
Message-ID: <1350005@otter.HP.COM>
Another question about idimatic and efficient code in CL.

Suppose I wish to constrauct a list with conditional components, that is,
components that may or may not be absent. [The actual application I had 
with this in was constructing a list of menu items, where some items were
only appropriate in certain circumstances].

The nearest I seem to be able to get is

    (append
        ... list of some boring bits ...
        (if Condition1 Bit1 '())
        ... list of more boring bits ...
        (if Condition2 Bit2 '())
        .... list of yet more boring bits ...
    )

or some splicing version likely to compile to similar code. This doesnt seem
too efficient (or nice to write, either!), even using -nconc- rather than
-append- - any comments, suggestions, or whatever?

Regards,
Kers                           | "Why Lisp if you can talk Poperly?"

---------------------------------------------------------------------------
I thought you'd never ask.

    [%
        ... boring bits ... [note: the elements, not a list thereof]
        if Condition1 then Bit1 endif;
        ... more boring bits ...
        if Condition2 then Bit2 endif;
        ... final boring bits ...
    %]

No garbage.

From: Juergen Wagner
Subject: Re: CL question - conditional list elements
Date: 
Message-ID: <2002@russell.STANFORD.EDU>
Conditional elements in lists (which can be determined at compile time)
are easy to write using the backquote syntax:

	`(bit1 bit2
	  ,(and (need-bit3) 'bit3)
	  bit 4 bit5
	  ,(and (need-bit4) 'bit4)
	  bit5 bit6)

I assume, you are aware of this possiblility. However, if you are intending
to sort of tag list elements to restrict their visibility, and to make the
accessibility of list elements context dependent, this should be handled
in an application-dependent way inside the menu handler, or whatsoever used
to interprete these lists. Lists changing from context to context (in 
particular, without external operations being performed) can cause severe
inconsistencies in your program. Imagine taking the (conditional) Car of a
list into a temporary variable. Then you call a function, then another one.
The validity of the isolated Car is questionable after the first call.
Therefore, I suggest to leave such problems up to the application.

-- 
Juergen Wagner,			        ·······@Russell.Stanford.edu
Center for the Study of Language and Information (CSLI), Stanford CA
From: Richard A. O'Keefe
Subject: Re: CL question - conditional list elements
Date: 
Message-ID: <591@cresswell.quintus.UUCP>
In article <····@russell.STANFORD.EDU>,
·······@russell.STANFORD.EDU (Juergen Wagner) writes:
> Conditional elements in lists (which can be determined at compile time)
> are easy to write using the backquote syntax:
> 
> 	`(bit1 bit2
> 	  ,(and (need-bit3) 'bit3)
> 	  bit 4 bit5
> 	  ,(and (need-bit4) 'bit4)
> 	  bit5 bit6)
> 
Wrong.  Suppose need-bit3 is false; this code will put NIL in the list.
The original poster wanted NOTHING in the list.
	,@(if (need-bit3) '(bit3) '())
will result in the right value.  But the original poster wanted a RUN-
time form, and was concerned about efficiency, and you are not allowed
to make any assumptions about what the CL reader turns backquote forms
into.  (Some implementations produce a more-or-less verbatim copy of the
input and interpret it at run-time.  Doubtless as many more generate
superb code.)  The original poster wanted something he could KNOW
wouldn't do any pointless consing.  Another point is that the Pop
version is GUARANTEED to return new cons cells for the list; it can
freely be altered without fear of smashing another copy.  The backquote
version may share some of the structure.

The giveaway was the Pop version.
	[% a, b, if t1 then c endif, d if t2 then e endif, f, g %]
operates like this:
	push a magic marker on the stack
	push a
	push b
	if t1 then push c endif
	push d
	if t2 then push e endif
	push f
	push g
	make a list of everything from the top of the stack
		down to the magic marker.
{It is possible for the code between the decorated brackets [% %]
 to try to pop the magic marker.  Forth is all the bad ideas from
 Pop without any of the good ideas (:-).}
The same trick can be used for making vectors and strings and other
things.  In fact there are neat things like
	[% SomeVector.destvector %] -> SomeVectorAsAList

Now I am not too sure whether the original poster was entirely
serious, or whether he was poking a finger at the clumsiness of Lisp.
The general answer to questions of the form "here's something I can
do in Pop, how do I do it in CL" is "you learn how to write Lisp and
then you won't find yourself wanting it."  Of course the same is true
in the other direction.  The other general answer seems to be "write
a macro..."

Here's the maybe-list macro I suggested as an answer.
It has the same properties as the Pop version.  (Note that as in my
maybe-cons, I assume that the thing is defined in my: package and
that my:R is not exported.)  This has been tested (somewhat).

(defmacro maybe-list (&rest L)
    `(let ((my:R '()))
        ,@(expand-maybe-list L)))

(defun expand-maybe-list (L)
    (cond
        ((endp L)                       ; L = ()
            nil)
        ((endp (cdr L))                 ; L = (x)
            `((setq my:R (list ,(car L)))))
        ((eq (cadr L) ':if)             ; L = (x :if y . z)
            `(,@(expand-maybe-list (cdddr L))
                (if ,(caddr L) (setq my:R (cons ,(car L) my:R)))))
        (t                              ; L = (x y . z)
            `(,@(expand-maybe-list (cdr L))
                (setq my:R (cons ,(car L) my:R))))))

;;; Example (it prints (A B E F)):
(print (maybe-list
	'a
	'b :if T
	'c :if NIL
	'd :if NIL
	'e :if T
	'f
))

The Pop version can contain loops as well.  This I do *not* propose
writing a macro for.  Some things are better not translated into Lisp.
From: Richard A. O'Keefe
Subject: Re: CL question - conditional list elements
Date: 
Message-ID: <589@cresswell.quintus.UUCP>
In article <·······@otter.HP.COM>, ····@otter.HP.COM (Christopher Dollin) writes:
> Another question about idimatic and efficient code in CL.
> 
> Suppose I wish to constrauct a list with conditional components, that is,
> components that may or may not be absent. [The actual application I had 
> with this in was constructing a list of menu items, where some items were
> only appropriate in certain circumstances].
> 
> The nearest I seem to be able to get is
> 
>     (append
>         ... list of some boring bits ...
>         (if Condition1 Bit1 '())
>         ... list of more boring bits ...
>         (if Condition2 Bit2 '())
>         .... list of yet more boring bits ...
>     )
> 
> or some splicing version likely to compile to similar code. This doesnt seem
> too efficient (or nice to write, either!), even using -nconc- rather than
> -append- - any comments, suggestions, or whatever?
> 

Why not use a macro, such as

	(defmacro maybe-cons (Test Datum Rest)		; tested in
	   `(let ((my:R ,Rest))				; Xlisp 1.6
		(if ,Test (cons ,Datum my:R) my:R)))

and then do
	(cons boring-bit-a
	(maybe-cons condition-1 bit-1
	(cons boring-bit-b
	(maybe-cons condition-2 bit-2
	(cons boring-bit-c
		nil)))))
This results in a pile of parens at the end, which is no problem with
a structure editor or paren-balancing, but if that's a problem, define
a maybe-list macro with calls like
	(maybe-list
	    boring-bit-a
	    bit-1 :if condition-1
	    boring-bit-b
	    bit-2 :if condition-2
	    boring-bit-c
	)
maybe-cons shows how to do this.  Of course, it does result in the
list elements being evaluated from right to left rather than from
left to right, but that shouldn't be a problem.
From: Jeff Dalton
Subject: Re: CL question - conditional list elements
Date: 
Message-ID: <243@aiva.ed.ac.uk>
In article <·······@otter.HP.COM> ····@otter.HP.COM (Christopher Dollin) writes:
>Another question about idimatic and efficient code in CL.
>
>Suppose I wish to constrauct a list with conditional components, that is,
>components that may or may not be absent.

As far as I can see, the only advantage to the Pop solution is a
somewhat nicer notation.  In Lisp, you may end up writing out some
code that does what you want instead.  But the code isn't particularly
bad.  Saying "it doesn't look too efficient (or nice to write!)"  is
just saying "I like Pop's syntactic sugar better than unsweetened
Lisp."  Nothing wrong with that, but one may reasonably disagree.

----------------------------------------------------------------------
I thought you'd never ask.

    `(
        ... boring bits ... [note: the elements, not a list thereof]
        ,@(if Condition1 (list Bit1))
        ... more boring bits ...
        ,@(if Condition2 (list Bit2))
        ... final boring bits ...
     )
----------------------------------------------------------------------

Jeff Dalton,                      JANET: ········@uk.ac.ed             
AI Applications Institute,        ARPA:  ·················@nss.cs.ucl.ac.uk
Edinburgh University.             UUCP:  ...!ukc!ed.ac.uk!J.Dalton