Lisp + observation... (unless)

Started by kanen, April 29, 2011, 06:35:28 PM

Previous topic - Next topic

kanen

I've been [re]learning Lisp (not newLisp) a little bit over the last few weeks, while writing my Lessons in Lisp stuff and I stumbled across a Language Popularity index http://www.langpop.com/#normalized">//http://www.langpop.com/#normalized a Wikipedia page about Common Lisp http://en.wikipedia.org/wiki/Common_Lisp">//http://en.wikipedia.org/wiki/Common_Lisp.



I'd seen both before, but I was struck by something specific.



The Common Lisp (until) macro example from the Wiki entry:



(until (= (random 10) 0)
  (write-line "Hello"))


I tried to think about how I might represent both the actual value and the number of iterations in Common Lisp.



In newLisp, it's a piece of cake (there are probably even easier ways to do this).



(until (= (set 'x (rand 10)) 0)
  (println  $idx ":" x " not 0"))


Just sharing a thought or two...
. Kanen Flowers http://kanen.me[/url] .

Kazimir Majorinc

#1
In Common Lisp? Something like that:


(defmacro until (test &body body)
  (let ((start-tag (gensym "START"))
        (end-tag   (gensym "END")))  
    `(progn (setf $idx 0)
            (tagbody ,start-tag
                    (when ,test (go ,end-tag))
                    (progn ,@body)
                    (incf $idx)
                    (go ,start-tag)
                    ,end-tag))))

(until(= (setf x (random 10)) 0)
      (format t "~d: ~d not 0 ~c" $idx x #Newline))
http://kazimirmajorinc.com/\">WWW site; http://kazimirmajorinc.blogspot.com\">blog.

nallen05

#2
hello kanen!



so your question is how to implement a varient of the UNTIL macro in Common Lisp that saves:

1. the return value of the test expression to a variable, and

2. the iteration count to a variable



note that in your example:



(until (= (set 'x (rand 10)) 0)
  (println  $idx ":" x " not 0"))


you are NOT saving the return value of the test, you are saving the value of "X", which is used inside the test form



sticking with your example, the easiest way to impliment it with the built-in functionality of common lisp is to use the LOOP macro:



(loop for x = (random 10)
  counting x into i
  do (format t "~a: ~a not 0~%" (1- i) x)
  until (= x 0))


but a lot of people don't like to use the extended LOOP form, since it's not pure s-expression language



believe it or not the DO macro is considered a nicer way to do it:



(do ((x #1=(random 10) #1#)
 (i 0 (1+ i)))
((= x 0))
  (format t "~a: ~a not 0~%" i x))


Kazmir's example is an implementation of the common UNTIL macro. This specific implementation saves the iteration count in the variable $IDX:



(defmacro until (test &body body)
  (let ((start-tag (gensym "START"))
        (end-tag   (gensym "END")))  
    `(progn (setf $idx 0)
            (tagbody ,start-tag
                    (when ,test (go ,end-tag))
                    (progn ,@body)
                    (incf $idx)
                    (go ,start-tag)
                    ,end-tag))))


in use:



(until(= (setf x (random 10)) 0)
      (format t "~d: ~d not 0 ~c" $idx x #Newline))


but this macro will fail if there is a nested UNTIL, because it will mess up the value of the outer $IDX. It can be modified to address this issue:



(defmacro until (test &body body)
  (let ((start-tag (gensym "START"))
        (end-tag   (gensym "END")))
    `(let (($idx 0))                        ;
       (tagbody ,start-tag
                (when ,test (go ,end-tag))
                (progn ,@body)
                (incf $idx)
                (go ,start-tag)
                ,end-tag))))


this macro could be done a little more simply by not using tagbody,

which is a considered a low-level-only-use-it-when-you-really-need-it special form,

and instead using a more simple, pre-baked iteration/control-flow macro (in this example LOOP

is considered a simple, pre-baked iteration/control-flow macro because there are no special keywords used--in this usage all the macro does is repeately execute it's arguments over and over and over again):



(defmacro until (test &body body)
  `(let (($idx 0))
      (loop (if ,test                              ;                  
                   (return)                        ;
                   (progn ,@body (incf $idx)))))) ;


so... none of these macros actually implement what you propose... they don't save the value of the test result. we can modify our UNTIL macro to do this:



(defmacro auntil (test &body body)
  `(let ($it                                  ;
           ($idx 0))
     (loop (if (setf $it ,test)                ;
  (return)
  (progn,@body (incf $idx))))))


Note that I named this macro AUNTIL, which is a common, but optional, convention for "anaphoric" macros:



http://www.bookshelf.jp//texi/onlisp/onlisp_15.html#SEC99">//http://www.bookshelf.jp//texi/onlisp/onlisp_15.html#SEC99



remember xetroxon's recent post about "The Definition of Simple is a Complex Thing"?



http://newlispfanclub.alh.net/forum/viewtopic.php?f=5&t=3863">//http://newlispfanclub.alh.net/forum/viewtopic.php?f=5&t=3863



In it he referenced Cal Sassenrath's definition of simple:


Quote
Simple is:

1. Clear abstraction: smart, well-drawn layers of "knowledge focus" that allow hiding of details.

2. Clean expression: meaningful, concise but not cryptic, representation and communication of concepts.


this definition is a good guide for us to use to reimplement AUNTIL in a more "lispy" way:



(defmacro until (test &body body)
`(loop (if ,test
  (return)
  (progn ,@body))))

(defmacro auntil (test &body body)
  `(let ($it
($idx 0))
     (until (setf $it ,test)
       ,@body
  (incf $idx))))


we broke our macro up into two well-drawn layers: an "until" macro that implements the control flow, and a more abstract AUNTIL that adds the anaphoric properties. This gives us Simple #1 (clearer abstracion). and as a corollary, our DEFMACRO forms become simpler, giving us Simple #2 (clean expression).



One cool thing about lisp's uniform semi-concise syntax (Lisp in the canonical sense, so including common lisp & newlisp) is that you can build the language up to be able to represent any part of your program you want semi-concisely in this uniform syntax:



(defmacro while (test &body body)
  `(loop (if ,test
      (progn ,@body)
(return))))

(defmacro awhile (test &body body)
  `(let ($it
         ($idx 0))
     (while (setf $it ,test)
,@body
(incf $idx))))

(defun not-zero (n)
  (unless (= n 0)
n))

(defmacro until-0 (form &body body)
  `(awhile (not-zero ,form)
,@body))

   

then your example becomes:



(until-0 (random 10)
  (format t "~a: ~a not 0~%" $idx $it))


0: 7 not 0

1: 5 not 0

2: 4 not 0

3: 9 not 0

4: 6 not 0

5: 6 not 0

6: 8 not 0

7: 8 not 0

8: 5 not 0

9: 1 not 0

10: 2 not 0

11: 8 not 0

NIL



in newLisp you could also build up the language to include until-0:



(define-macro (until-0)
  (letex (zero-form (args 0)
   body      (cons 'begin (1 (args))))
   (let ($num nil)
(until (= 0 (setq $num zero-form))
body))))


and then your example becomes more simple:



(until-0 (rand 10)
  (println  $idx ":" $num " not 0"))




0:2 not 0

1:9 not 0

2:9 not 0

3:8 not 0

4:2 not 0

5:5 not 0

6:3 not 0

7:7 not 0

8:5 not 0

9:6 not 0

10:5 not 0



Anyway, hope this helps



Take care



Nick