Hi,
> (let ((x '(a b c d))) (loop repeat 2 for x in x collect x))
>==> (a b)
Sure. Where is the problem? I see no problem at all. That's the semantic I want to give it.
I still like showing the macroexpansion, because it allows to express the semantics I want to give to loop initialization.
(let ((x '(a b c d)))
(block nil
(let ((collector ()))
(let ((_from 1) (_to 2)) ; left to right
(let ((x _from)) ; second pass
(let (y) ; initialization delayed until body
(tagbody ...
Whether x and _from shall collapse is to be debated separately, below.
Here's some vocabulary I made up:
Driver Variable(s)
The main variable(s) that permit iteration, e.g. the array index FOR # ACROSS.
One may or not consider the vector that is constant through the iteration as a
driver variable too.
Derived Variables
Those are derived from driver variables, e.g. all variables in a true destructuring pattern.
A "true" destructuring pattern is a list, i.e. either nil or a cons, not another symbol.
Named variables
Those explicitly named by the user of LOOP.
Note that this also includes collecting INTO # etc. but that's irrelevant for now.
Clearly in FOR x IN ..., x is nevertheless a derived variable of some internal
driver variable necessary to hold the list whose CAR is set to x during iteration.
FOR x ON ... is peculiar, because users expect x to *be* the driver variable. So we
shall meet that expectation as a special rule.
Regarding for-as-arithmetic, it can be debated whether we should make driver and named variable collapse.
That is related to the FINALLY discussion.
A) If they don't collapse, then we can assert
(for x of-type (or (integer 2 10) (eql 0)) from 2 to 10)
(for x of-type (or (integer 2 (10)) (eql 0)) from 2 below 10)
because the internal driver variable will be tested against the limits
and x will be stepped only when the iteration does not terminate.
Hence that is the value that will *likely* be available within FINALLY.
I said "likely" because some other clauses may cause termination even earlier.
(EQL 0) is needed because of the default binding of x.
B) Many people (clearly *not* everybody) expect them to collapse.
Consequently, that variable is *likely* to be stepped beyond limits
(for x of-type (integer 0 (10)) from 0 below 10) ; x is likely to end at 10
I said "likely", because one could implement the test as follows
(when (>= (+ x _step) _limit) (loop-finish))
(setq x (+ x _step)); i.e. perform the addition twice
(or use anaphoric if to avoid that duplication, or
C) cache it directly
(setq _helpvar (+ x _step))
(when (>= _helpvar _limit) (loop-finish))
(setq x _helpvar)
(That is almost, but not completely undistinguishable from A)
Then one can assert the type (integer 2 (10)), without (eql 0) --
well mostly. Consider the degenerate case (for x from 2 below 2).
X would be bound to 2, but it's type would not satisfy (integer 2 (2)),
but merely (integer 2 2).
For completeness, collapsing can only occur when the variable is named.
(loop for nil below 2 count t) ; is valid, ==>2
In summary the choice is ours.
That's three "proposals" just to discuss for-as-arithmetic!
My preference is
1. Dependability, i.e. provide understandable semantics that people can depend upon.
2. Performance - don't make it overly complicated for perceived over-correctness.
Hence I'd directly step x over limits, then perform the termination test.
Incidentally, that's what clisp has been doing for decades :-)
Benefits:
- (A/B/C) Execution model and effects are simple to understand
- (B/C) x gets a defined value right from the start (FROM/DOWNFROM), not an arbitrary 0.
Heck, perhaps I should favour C. The implementation is minimally slower (hopefully 1 bytecode), however it's
Benefit:
- (C) Type declaration given by bounds (except in degenerate case)
(for x of-type (integer 2 10) from 2 to 10)
(for x of-type (integer 2 (10)) from 2 below 10)
But clisp doesn't care about the type, so why make it more complicated?
Hence B is a perfect choice for clisp, while C might be better for CMUCL.
C *might* have another benefit:
- (C) Even easier to explain FINALLY value: "last value", independent on the ordering of termination tests.
But that's too early, we need to discuss sequencing/interleaving of stepping and termination tests first.
I'll be back mid-January, hopefully with code. Take care,
Jörg
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
clisp-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/clisp-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not
affiliated with the servers or forums shown here and is not responsible for
the content of articles, which is written by their respective authors.