kers@otter.HP.COM (Christopher Dollin) (01/14/88)
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.
gandalf@russell.STANFORD.EDU (Juergen Wagner) (01/28/88)
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, gandalf@Russell.Stanford.edu Center for the Study of Language and Information (CSLI), Stanford CA
ok@quintus.UUCP (Richard A. O'Keefe) (01/28/88)
In article <1350005@otter.HP.COM>, kers@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.
ok@quintus.UUCP (Richard A. O'Keefe) (01/28/88)
In article <2002@russell.STANFORD.EDU>, gandalf@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.
jeff@aiva.ed.ac.uk (Jeff Dalton) (01/28/88)
In article <1350005@otter.HP.COM> kers@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: J.Dalton@uk.ac.ed AI Applications Institute, ARPA: J.Dalton%uk.ac.ed@nss.cs.ucl.ac.uk Edinburgh University. UUCP: ...!ukc!ed.ac.uk!J.Dalton
jeff@aiva.ed.ac.uk (Jeff Dalton) (01/30/88)
In article <591@cresswell.quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes: >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. Oh, so it's pushing on the stack we want. That can be done: 1> (defmacro include-if (test values) `(if ,test ,values (values))) INCLUDE-IF 2> (multiple-value-call #'list 1 2 3 4 5 (include-if t (values 6 7)) 8 9 10 (include-if nil (values 11 12)) 13 14 15) (1 2 3 4 5 6 7 8 9 10 13 14 15) Is that enough? I can write the elements not a list thereof. It's guaranteed to build a new list. It doesn't officially generate any garbage. (Does someone complain that it's not GUARANTEED not to produce garbage? Such guarantees are easy for PopLog because there's only one implementation.) >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..." Some people, I think, will always want some things even after they learn how to write Lisp. After all, Lisp can't be all things to all people. Jeff Dalton, JANET: J.Dalton@uk.ac.ed AI Applications Institute, ARPA: J.Dalton%uk.ac.ed@nss.cs.ucl.ac.uk Edinburgh University. UUCP: ...!ukc!ed.ac.uk!J.Dalton
miller@ACORN.CS.ROCHESTER.EDU (Brad Miller) (01/30/88)
Date: 28 Jan 88 11:13:45 GMT From: ok@quintus.UUCP (Richard A. O'Keefe) 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 )) possibly more true to the original syntax would be: (let (foo) (push 'a foo) (if condition (push 'b foo)) foo) and similarly could be marcoized if desired... if the original order is needed, return (nreverse foo) instead of foo... Besides, who cares about efficiency? THe compiler is supposed to worry about that not the programmer... (only 1/4 :-)) ------ miller@cs.rochester.edu {...allegra!rochester!miller} Brad Miller University of Rochester Computer Science Department 'If anything is True, this is.'