Lisp + observation... (unless)

Q&A's, tips, howto's
Locked
kanen
Posts: 145
Joined: Thu Mar 25, 2010 6:24 pm
Contact:

Lisp + observation... (unless)

Post by 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 a Wikipedia page about 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:

Code: Select all

(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).

Code: Select all

(until (= (set 'x (rand 10)) 0) 
  (println  $idx ":" x " not 0"))
Just sharing a thought or two...
. Kanen Flowers http://kanen.me .

Kazimir Majorinc
Posts: 388
Joined: Thu May 08, 2008 1:24 am
Location: Croatia
Contact:

Re: Lisp + observation... (unless)

Post by Kazimir Majorinc »

In Common Lisp? Something like that:

Code: Select all

(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))

nallen05
Posts: 21
Joined: Sun Apr 19, 2009 10:12 pm

Re: Lisp + observation... (unless)

Post by nallen05 »

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:

Code: Select all

(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:

Code: Select all

(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:

Code: Select all

(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:

Code: Select all

(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:

Code: Select all

(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:

Code: Select all

(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):

Code: Select all

(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:

Code: Select all

(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/on ... html#SEC99

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

http://newlispfanclub.alh.net/forum/vie ... f=5&t=3863

In it he referenced Cal Sassenrath's definition of simple:
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:

Code: Select all

(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:

Code: Select all

(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:

Code: Select all

(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:

Code: Select all

(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:

Code: Select all

(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

Locked