From: Kim Russell
Subject: defmacro
Date: 
Message-ID: <1148315985.747065.23700@y43g2000cwc.googlegroups.com>
Hi,

I'm writing a defmacro which expands to a do loop and calls an
iterator. I think I have most of it done but the do syntax is kind of
confusing.
Any help would be appreciated. I apologize if it's not the best syntax.

(defmacro doloop (vars &body body)
  (let ((arg (gensym))
        (itr (gensym)))
    `(let ((,arg ',(car vars))
           (,itr ',(cadr vars)))
       (do ((,arg() (funcall (iter-n ,itr)))
           ((,v ,arg) ((null (funcall (iter-has-n ,itr)) arg))))
            ,@body))))

kim

From: Kim Russell
Subject: Re: defmacro
Date: 
Message-ID: <1148316199.261846.217720@u72g2000cwu.googlegroups.com>
sorry I forgot to mention that when i run it:

(doloop (n (list-itr '(a 2 3 4)))
     (format t "~A~%" n))

it should print:
a
2
3
4
From: Wolfram Fenske
Subject: Re: defmacro
Date: 
Message-ID: <1148414449.232636.127440@u72g2000cwu.googlegroups.com>
"Kim Russell" <······@gmail.com> writes:

> (doloop (n (list-itr '(a 2 3 4)))
>      (format t "~A~%" n))
>
> it should print:
> a
> 2
> 3
> 4

Here is my take:

--8<---------------cut here---------------start------------->8---
(defmacro doloop ((arg iterexpr) &body body)
  (let ((loop (gensym))
	(end  (gensym))
	(iter (gensym)))
    `(let (,arg
	   (,iter ,iterexpr))
       (tagbody
	,loop
	(cond ((iter-has-next ,iter)
	       (setf ,arg (iter-get-next ,iter)))
	      (t (go ,end)))
	,@body
	(go ,loop)
	,end))))
--8<---------------cut here---------------end--------------->8---

It's not very pretty or sophisticated but IMO it's easier to
understand than a solution using DO (which expands to TAGBODY/GO
anyway).  Essentially, my solution is similar to a WHILE loop in C.
If you think it's too raw, you can write a WHILE macro first and then
write DOLOOP somewhat more elegantly.


Cheers
Wolfram
From: Wolfram Fenske
Subject: Re: defmacro
Date: 
Message-ID: <1148415580.843500.255690@i40g2000cwc.googlegroups.com>
"Wolfram Fenske" <·····@gmx.net> writes:

> If you think it's too raw, you can write a WHILE macro first and then
> write DOLOOP somewhat more elegantly.

... perhaps like this:

--8<---------------cut here---------------start------------->8---
(defmacro while (condition &body body)
  (let ((loop (gensym))
	(end  (gensym)))
    `(tagbody
      ,loop
      (unless ,condition
	(go ,end))
      ,@body
      (go ,loop)
      ,end)))

(defmacro doloop ((arg iterexpr) &body body)
  (let ((iter (gensym)))
    `(let (,arg
	   (,iter ,iterexpr))
       (while (iter-has-next ,iter)
	 (setf ,arg (iter-get-next ,iter))
	 ,@body))))
--8<---------------cut here---------------end--------------->8---


Cheers
Wolfram
From: Thomas A. Russ
Subject: Re: defmacro
Date: 
Message-ID: <ymiwtcdk267.fsf@sevak.isi.edu>
"Kim Russell" <······@gmail.com> writes:

> Hi,
> 
> I'm writing a defmacro which expands to a do loop and calls an
> iterator. I think I have most of it done but the do syntax is kind of
> confusing.


Well, maybe it would be easier to expand into LOOP code....

> Any help would be appreciated. I apologize if it's not the best syntax.

It can probably be simplified a bit depending on your conventions.  For
example, if the argument variable for the iteration has to be a SYMBOL
(which is a reasonable expectation), you don't need to go through the
trouble of creating a GENSYM for it and binding it to the unevaluated
result.  You can use it dirctly.  Also, since you presumably want to
evaluate the iterator argument, you don't want to quote it.  You do want
to use a GENSYM for that, since it would be bad to evaluate it multiple
times.

Also, you can take advantage of argument list destructuring in Macro
lambda argument lists.  DEFMACRO is more flexible in that way than

> (defmacro doloop (vars &body body)
>   (let ((arg (gensym))
>         (itr (gensym)))
>     `(let ((,arg ',(car vars))
>            (,itr ',(cadr vars)))
>        (do ((,arg() (funcall (iter-n ,itr)))
>            ((,v ,arg) ((null (funcall (iter-has-n ,itr)) arg))))
>             ,@body))))

Of course, one of the big problems is that the parentheses don't
properly balance for the DO loop.  From the indenting, I think you want
the first clause to be simply the stepping code for the iteration.  The
end test is presumably the code done with the NULL.

Also, it isn't at all clear that FUNCALL is needed, but since we don't
really have any clue as to what the interface to your iterators is, it's
bit hard to tell for sure.  But I suspect you don't want to use FUNCALL
at all.

Finally, there is this ,v that refers to an unbound argument.

Perhaps this untested code is closer to what you want.  I'm not
generally up on DO syntax, since I hate it and use LOOP instead, so
there may be some bugs.

(defmacro doloop ((var iterator-form) &body body)
  (let ((itr (gensym)))
    `(let ((,itr ,iterator-form))
       (do ((,var () (iter-n ,itr)))
	   ((null (iter-has-n ,itr)) ,var)
	 ,@body))))

For what it's worth, here is a LOOP version:

(defmacro doloop ((var iterator-form) &body body)
  (let ((itr (gensym)))
    `(let ((,itr ,iterator-form))
       (loop for ,var = (iter-n ,itr)
	   while (iter-has-n ,itr)
	   do ,@body))))

In fact, I find the LOOP body to be sufficiently simple that I wouldn't
really bother with a macro at all and just write the code itself.

But the macro does save some setup.  I would, however consider calling
it DO-ITERATION rather than DOLOOP.

-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Tayssir John Gabbour
Subject: Re: defmacro
Date: 
Message-ID: <1148356504.506368.293920@g10g2000cwb.googlegroups.com>
Thomas A. Russ wrote:
> (defmacro doloop ((var iterator-form) &body body)
>   (let ((itr (gensym)))
>     `(let ((,itr ,iterator-form))
>        (loop for ,var = (iter-n ,itr)
> 	   while (iter-has-n ,itr)
> 	   do ,@body))))

I wonder whether the original poster's iterators are anything like
Java's, where we're supposed to test iterator.hasNext() before
executing iterator.next(). That is,

(loop while (iter-has-next iterator)
      for next = (iter-next iterator)
      do ...)

Unfortunately, since we can't have a WHILE clause before a FOR [1], we
do something a little inelegant like:

(loop while (iter-has-next iterator)
      do (let ((next (iter-next iterator)))
           ...))


(Maybe ITERATE doesn't have this problem, but it actually doesn't
support parallel stepping... ugh, I wonder if it's extensible enough to
get it back...)


[1] http://www.lispworks.com/documentation/HyperSpec/Body/m_loop.htm


#\Tayssir
From: Thomas A. Russ
Subject: Re: defmacro
Date: 
Message-ID: <ymik68ck6mt.fsf@sevak.isi.edu>
"Tayssir John Gabbour" <···········@yahoo.com> writes:

> I wonder whether the original poster's iterators are anything like
> Java's, where we're supposed to test iterator.hasNext() before
> executing iterator.next(). That is,

Veering off thread here, I have a minor rant about the Java iterator
model.

What's really sad about it is that the way it is implemented, makes
certain extensions needlessly difficult to support.

The interaction model is the following:
  (a) You may ask as often as you like if more items are available.
  (b) You can only get the value once per iteration because getting the
      value automatically advances the iterator.

I can see how having some automatic advance makes using iterators a bit
more friendly, but I would have preferred separating the two operations
for clarity.  That would have a couple of benefits:

  (1) You could retrieve the value more than once without advancing the
      iterator, instead of being forced to bind a variable to the value.
  (2) It would support extensions to the iteration so that one could,
      say iterate over more complicated structures such as hash tables
      and get both the key and the value (instead of just one).
  (3) One could still support the combined operation with a new one that
      could be called getValueAndAdvance().

Number (2) could be achieved in the current iteration model, but it
would be fraught with tricky special perils.  For example, one would
have to be careful to access all of the other information (such as
getKey) before calling getValue or else you would have mismatches.



-- 
Thomas A. Russ,  USC/Information Sciences Institute
From: Pascal Bourguignon
Subject: Re: defmacro
Date: 
Message-ID: <873bf07e1b.fsf@thalassa.informatimago.com>
···@sevak.isi.edu (Thomas A. Russ) writes:

> "Tayssir John Gabbour" <···········@yahoo.com> writes:
>
>> I wonder whether the original poster's iterators are anything like
>> Java's, where we're supposed to test iterator.hasNext() before
>> executing iterator.next(). That is,
>
> Veering off thread here, I have a minor rant about the Java iterator
> model.
>
> What's really sad about it is that the way it is implemented, makes
> certain extensions needlessly difficult to support.
>
> The interaction model is the following:
>   (a) You may ask as often as you like if more items are available.
>   (b) You can only get the value once per iteration because getting the
>       value automatically advances the iterator.
>
> I can see how having some automatic advance makes using iterators a bit
> more friendly, but I would have preferred separating the two operations
> for clarity.  That would have a couple of benefits:
>
>   (1) You could retrieve the value more than once without advancing the
>       iterator, instead of being forced to bind a variable to the value.
>   (2) It would support extensions to the iteration so that one could,
>       say iterate over more complicated structures such as hash tables
>       and get both the key and the value (instead of just one).
>   (3) One could still support the combined operation with a new one that
>       could be called getValueAndAdvance().
>
> Number (2) could be achieved in the current iteration model, but it
> would be fraught with tricky special perils.  For example, one would
> have to be careful to access all of the other information (such as
> getKey) before calling getValue or else you would have mismatches.

Well, you can always apply some _pattern_:

(defgeneric make-smart-iterator       (iterator))
(defgeneric get-next-item-and-advance (smart-iterator))
(defgeneric more-item-available-p     (smart-iterator))
(defgeneric get-current-item          (smart-iterator))

(defclass smart-iterator ()
   ((current-item :reader get-current-item)
    (iterator     :reader dumb-iterator))

(defmethod get-next-item-and-advance ((self smart-iterator))
    (setf (slot-value self 'current-item) 
          (get-next-item (dumb-iterator self))))

...


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/

The world will now reboot.  don't bother saving your artefacts.
From: Tayssir John Gabbour
Subject: Re: defmacro
Date: 
Message-ID: <1148444295.024299.303130@j33g2000cwa.googlegroups.com>
Thomas A. Russ wrote:
> Veering off thread here, I have a minor rant about the Java iterator
> model.
>
> What's really sad about it is that the way it is implemented, makes
> certain extensions needlessly difficult to support.
>
> The interaction model is the following:
>   (a) You may ask as often as you like if more items are available.
>   (b) You can only get the value once per iteration because getting the
>       value automatically advances the iterator.
>
> I can see how having some automatic advance makes using iterators a bit
> more friendly, but I would have preferred separating the two operations
> for clarity.  That would have a couple of benefits:
>
>   (1) You could retrieve the value more than once without advancing the
>       iterator, instead of being forced to bind a variable to the value.
>   (2) It would support extensions to the iteration so that one could,
>       say iterate over more complicated structures such as hash tables
>       and get both the key and the value (instead of just one).
>   (3) One could still support the combined operation with a new one that
>       could be called getValueAndAdvance().

Yes, perhaps an interator could be conceptually like READ; it could add
the operation peek(), maybe a retractLast(). It could keep hasNext()
and next().


Tayssir
From: Pascal Bourguignon
Subject: Re: defmacro
Date: 
Message-ID: <87fyj27yto.fsf@thalassa.informatimago.com>
"Kim Russell" <······@gmail.com> writes:

> Hi,
>
> I'm writing a defmacro which expands to a do loop and calls an
> iterator. I think I have most of it done but the do syntax is kind of
> confusing.
> Any help would be appreciated. I apologize if it's not the best syntax.
>
> (defmacro doloop (vars &body body)
>   (let ((arg (gensym))
>         (itr (gensym)))
>     `(let ((,arg ',(car vars))
>            (,itr ',(cadr vars)))
>        (do ((,arg() (funcall (iter-n ,itr)))
>            ((,v ,arg) ((null (funcall (iter-has-n ,itr)) arg))))
>             ,@body))))

You can use macroexpand and macroexpand-1:

[77]> (defmacro doloop (vars &body body)
  (let ((arg (gensym))
        (itr (gensym)))
    `(let ((,arg ',(car vars))
           (,itr ',(cadr vars)))
       (do ((,arg() (funcall (iter-n ,itr)))
           ((,v ,arg) ((null (funcall (iter-has-n ,itr)) arg))))
            ,@body))))
DOLOOP
[78]> (macroexpand '(doloop (n (list-itr '(a 2 3 4)))
                     (format t "~A~%" n)))

*** - EVAL: variable V has no value
The following restarts are available:
USE-VALUE      :R1      You may input a value to be used instead of V.
STORE-VALUE    :R2      You may input a new value for V.
ABORT          :R3      ABORT
Break 1 [79]> 


You can give names to gensym to make it more readable, and I removed
the comma before V to obtain the following to try to understand what
you're doing.

[87] (defmacro doloop (vars &body body)
  (let ((arg (gensym "ARG-"))
        (itr (gensym "ITR-")))
    `(let ((,arg ',(car vars))
           (,itr ',(cadr vars)))
       (do ((,arg() (funcall (iter-n ,itr)))
            ((v ,arg) ((null (funcall (iter-has-n ,itr)) arg))))
           ,@body))))

DOLOOP
[88]> (macroexpand '(doloop (n (list-itr '(a 2 3 4)))
                     (format t "~A~%" n)))
(LET ((#:ARG-5244 'N) (#:ITR-5245 '(LIST-ITR '(A 2 3 4))))
 (DO
  ((#:ARG-5244 NIL (FUNCALL (ITER-N #:ITR-5245)))
   ((V #:ARG-5244) ((NULL (FUNCALL (ITER-HAS-N #:ITR-5245)) ARG))))
  (FORMAT T "~A~%" N))) ;
T


I'm still puzzled.   To write a macro, instead of showing the result
of "calling" the macro, you should show an example of what the macro
should expand to:

  (doloop (n '(a b c)) (print n))

  --(expands-to)-->
 
  (do  ((n    '()      (first ,itr))
        (,itr '(a b c) (rest  ,itr)))
       ((null ,itr))
    (print n))

(showing with , the gensymed variables for example).
Then we could easily write a macro to do that:


(defmacro doloop ((var init-value) &body body)
  (let ((itr (gensym "ITR-")))
    `(do  ((,var    '()      (first ,itr))
           (,itr ,init-value (rest  ,itr)))
          ((null ,itr))
      ,@body)))

If you want something else, we need more explanations...


-- 
__Pascal Bourguignon__                     http://www.informatimago.com/
Until real software engineering is developed, the next best practice
is to develop with a dynamic system that has extreme late binding in
all aspects. The first system to really do this in an important way
is Lisp. -- Alan Kay