From: Joerg Hoehle
Subject: loop variable value in finally forms?
Date: 
Message-ID: <ufywr6s5g.fsf@users.sourceforge.net>
Hi,

I stumbled across the following:

(loop for l on '(a b c d) by #'cddr do (prin1 l) finally (print l))
(A B C D)(C D)
NIL ; <- in finally
NIL

(loop for (l) on '(a b c d) by #'cddr do (prin1 l) finally (print l))
AC
C  ; <- in finally
NIL

In the first loop above, the final value (NIL) is that computed after
the last step, where the list end is detected. L has already been
assigned (a third time).  In the second loop, the final value (C) is
that from the last iteration through the body, L has not been assigned
(or destructured) a third time.

Is that necessarily so?

I read the CLHS chapters on loop again and again, and I'm less and
less convinced that there are guarantees about the value of variables
inside finally. Especially, stepping and end-test seems to me like
they can occur in any order, since the cases for internal and external
(programmer visible) variables may be distinct.

So my conclusion is that the first loop above need not necessarily
exhibit l<-NIL in finally. Another correct loop implementation might
be:

(let* ((internal '(a b c d))
	(l internal))
  (tagbody ; block nil omited
    (go test)
    body (prin1 l)
    step1 (setf internal (cddr internal))
    test (when (endp internal) (go finally))
    step2 (setf l internal)
    (go body)
    finally (print l)))
  
That would not print l->NIL in finally.

What would you think?
	Jorg Hohle
Telekom/T-Systems Technology Center

From: Ulrich Hobelmann
Subject: Re: loop variable value in finally forms?
Date: 
Message-ID: <3ek59uF3ivo7U1@individual.net>
Joerg Hoehle wrote:
> Hi,
> 
> I stumbled across the following:
> 
> (loop for l on '(a b c d) by #'cddr do (prin1 l) finally (print l))
> (A B C D)(C D)
> NIL ; <- in finally
> NIL
> 
> (loop for (l) on '(a b c d) by #'cddr do (prin1 l) finally (print l))
> AC
> C  ; <- in finally
> NIL

Well, here you used "for l ON ...", not IN, so in the first l gets 
bound to all the cons cells while iterating: (a . ...) (c . ...) 
and nil.  In the second loop you have a destructuring value, so l 
gets bound to only the CAR of each cons cell.  I think the second 
is equivalent to
(loop for l in ...), but I'm not sure.

> In the first loop above, the final value (NIL) is that computed after
> the last step, where the list end is detected. L has already been
> assigned (a third time).  In the second loop, the final value (C) is
> that from the last iteration through the body, L has not been assigned
> (or destructured) a third time.
> 
> Is that necessarily so?

The form "(l)" (eqv to (l . nil)) does not match nil in the last 
iteration, because nil isn't a cons cell, so the second loop can't 
iterate another time.

But why aren't you just using "in"?

What are you trying to do?

-- 
Don't let school interfere with your education. -- Mark Twain
From: Kalle Olavi Niemitalo
Subject: Re: loop variable value in finally forms?
Date: 
Message-ID: <87mzqy4vx6.fsf@Astalo.kon.iki.fi>
Ulrich Hobelmann <···········@web.de> writes:

> The form "(l)" (eqv to (l . nil)) does not match nil in the last
> iteration, because nil isn't a cons cell, so the second loop can't
> iterate another time.

Are you implying that the destructuring pattern affects how many
times a for-as-on-list clause will iterate?

* (loop for () on '(a b c d e) count t)

5
* (loop for (x y z) on '(a b c d e) count t)

5
From: Ulrich Hobelmann
Subject: Re: loop variable value in finally forms?
Date: 
Message-ID: <3esfrkF4ou4pU2@individual.net>
Kalle Olavi Niemitalo wrote:
> Ulrich Hobelmann <···········@web.de> writes:
> 
> 
>>The form "(l)" (eqv to (l . nil)) does not match nil in the last
>>iteration, because nil isn't a cons cell, so the second loop can't
>>iterate another time.
> 
> 
> Are you implying that the destructuring pattern affects how many
> times a for-as-on-list clause will iterate?
> 
> * (loop for () on '(a b c d e) count t)
> 
> 5
> * (loop for (x y z) on '(a b c d e) count t)
> 
> 5

Hm, then why did OP's example only iterate one less time with "(l) 
on", while the "l in" CDDRed through the list and found nil at the 
end?  I'm clueless again.

-- 
Don't let school interfere with your education. -- Mark Twain
From: Joerg-Cyril Hoehle
Subject: Re: loop variable value in finally forms?
Date: 
Message-ID: <87oeb62fek.fsf@localhost.localdomain.i-did-not-set--mail-host-address--so-tickle-me>
Ulrich Hobelmann <···········@web.de> writes:
> Kalle Olavi Niemitalo wrote:
>> * (loop for () on '(a b c d e) count t)
>> 5
>> * (loop for (x y z) on '(a b c d e) count t)
>> 5

> Hm, then why did OP's example only iterate one less time with "(l)
> on", while the "l in" CDDRed through the list and found nil at the
> end?  I'm clueless again.
Huh, both examples looped the same number of times: 2

> What are you trying to do?

I came across the following example:

(defun my-memberl (elt list &key (test #'eql) (key #'identity))
  (loop for l on list
	until (funcall test elt (funcall key (car l)))
	finally (return l)))

It struck me, because I'd have never written the loop this way.
I believe Antonio Menezes Leitao wrote it this way on one of his
Amsterdam 2005 meeting slides.

I question the validity of that code, since I'm not convinced that l
is nil after the list is exhausted.

This argument is based on the following possible (agreed naive)
implementation of loop for ... on:

(loop for internal on '(a b c d) by #'cddr
      for l = internal ; for (l1 l2 l3) = internal with destructuring
      do (prin1 l)
      finally (print l))
(A B C D)(C D)
(C D) ; l in finally
NIL
Written that way, l is not nil inside the finally clause, unlike the
for l on ... case (at least in cmucl, sbcl and clisp).

This version makes for l on ... appear as a special-case optimization
on the more general destructuring for <pattern> on ... case, where the
internal and single user-given variables can be identical.

For example, the Iterate package currently behaves differently (it
effectively uses that internal variable). Thus, if I can be convinced
that CLHS mandates NIL for l in the finally section, I'll go and
change Iterate's behaviour.

Regards,
        Joerg Hoehle.