Loop for and behaviour

From: Antonio Menezes Leitao (Antonio.Leitao_at_evaluator.pt)
Date: 05/24/04

  • Next message: Mark McConnell: "Re: CL and iterators - a newbie question"
    Date: Mon, 24 May 2004 01:09:28 +0100
    
    

    Hi,

    After seeing so many complains regarding one of my challenges for
    translation from Common Lisp to Java I decided to investigate it. I
    must warn that I'm not a loop expert and all my loops were (and are)
    quite simple so I might be wrong about all this. I hope someone can
    clarify the issue for me. The challenge in question is the following:

    (defun test5 ()
      (let ((x 10))
        (loop for x from 1 to 9
              and y = x then x
              sum (+ x y))))

    I was under the impression that the 'and' loop keyword would cause x and y
    to be bound in parallel. In fact, the Hyperspec says:

     6.1.1.5.1 Summary of Variable Initialization and Stepping Clauses

     The for and as constructs provide iteration control clauses that
     establish a variable to be initialized. for and as clauses can be
     combined with the loop keyword and to get parallel initialization and
     stepping[1]. Otherwise, the initialization and stepping[1] are sequential.

    "Parallel initialization and stepping" sounded just like the difference
    between 'do' and 'do*'. And I confirmed this idea when I read:

     6.1.2.1 Iteration Control

     If multiple iteration clauses are used to control iteration, variable
     initialization and stepping[1] occur sequentially by default. The and
     construct can be used to connect two or more iteration clauses when
     sequential binding and stepping[1] are not necessary. The iteration
     behavior of clauses joined by and is analogous to the behavior of the
     macro do with respect to do*.

    Not being a loop expert, this made me think that the above loop should
    have been identical to the following:

    (defun test5 ()
      (let ((x 10))
        (let ((sum 0))
          (do ((x 1 (1+ x))
               (y x x))
              ((> x 9) sum)
            (incf sum (+ x y))))))

    However, everybody was complaining that the outer 'x' was an unused
    variable. I tried my example in a lot of Common Lisps and, in fact, they
    all agreed with everybody's opinion (except mine). Where was my error?

    After further reading the Hyperspec, I found the following possible
    explanation:

     6.1.1.4 Expanding Loop Forms

     Implementations can interleave the setting of initial values with the
     bindings. However, the assignment of the initial values is always
     calculated in the order specified by the user. A variable is thus
     sometimes bound to a meaningless value of the correct type, and then
     later in the prologue it is set to the true initial value by using setq.
     One implication of this interleaving is that it is
     implementation-dependent whether the lexical environment in which the
     initial value forms (variously called the form1, form2, form3, step-fun,
     vector, hash-table, and package) in any for-as-subclause, except
     for-as-equals-then, are evaluated includes only the loop variables
     preceding that form or includes more or all of the loop variables; the
     form1 and form2 in a for-as-equals-then form includes the lexical
     environment of all the loop variables.

    Note the exception made of the for-as-equals-then. It looks like the
    outer 'x' is, after all, unused, because the 'y' is within the scope
    of the inner 'x'. That's odd. I couldn't understand the rational for
    that behaviour so I digged a little further and I found the long
    discussion of the Issue
    LOOP-INITFORM-ENVIRONMENT:PARTIAL-INTERLEAVING-VAGUE but after reading it
    I became even more confused because it seems that it agrees with me. They
    even include the following example as a test case:

    (let ((list '(1 2 3)))
      (loop for list = list then (cdr list)
            until (null list)
            collect (car list)))
    => (1 2 3)

    If you try this, I bet your Common Lisp implementation will warn that the
    outer 'list' is unused and then the result is NIL and not (1 2 3).

    I noted that the Issue status is 'For X3J13 consideration' but I couldn't
    see if it was accepted or rejected. I presume it was rejected.

    So, I remain puzzled. Is there a rational for the 6.1.1.4 Expanding Loop
    Forms paragraph that I presented above?

    Thanks in advance,

    Antonio Leitao.


  • Next message: Mark McConnell: "Re: CL and iterators - a newbie question"

    Relevant Pages

    • Re: LOOP blows!
      ... At beginning of each iteration. ... All variables in the FOR clauses ... ] All variables are initialized in the loop prologue. ... Initialization is not assignment! ...
      (comp.lang.lisp)
    • Re: LOOP blows!
      ... Your LOOP is obviously broken. ... So X will get initialized to 'at the begining of each iteration. ... clauses are executed in the loop ... repeated until a clause terminates the loop or until a return, go, or ...
      (comp.lang.lisp)
    • Yow! LOOP macros are LOOPY!
      ... By relying entirely on procedure calls to express iteration, ... to but cleaner than C's FOR loop. ... other macros going around at the time other than MacLisp's ... (bind (vi (vector-ref v i))) ...
      (comp.lang.scheme)
    • Re: Polling, Interrupts, DMA, Synchronous, Asynchronous I/O Definitions
      ... the terminology is less useful than it might be. ... though a "message loop" could arguably be claimed to be ... considering in a particular iteration but what is true for _ALL_ ... watching the "polling" version eating up every single CPU cycle ...
      (alt.lang.asm)
    • Re: Histogram of character frequencies
      ... generated object code may simply be a loop in which elements are ... believe any C compiler anywhere would reject it. ... On the first iteration of the loop you test the end of file indicator ...
      (comp.lang.c)