  lua-l archive

• Subject: Re: [NoW] About numeric for-loop
• From: Dirk Laurie <dirk.laurie@...>
• Date: Thu, 17 Jan 2019 07:44:23 +0200

```Op Wo. 16 Jan. 2019 om 23:45 het Egor Skriptunoff
<egor.skriptunoff@gmail.com> geskryf:
>
> "NoW" means "Nitpicking on Wednesdays"
> (unimportant questions about Lua design and implementation)
>
>    for x = -2^53, -2^53 do print(x) end
>    for x = 1e100, 1e100 do print(x) end
> Did you expect such results?

This is just disguised version of hardy perennial on this list:
(a) relying on equality testing involving floating-point arithmetic
(b) blaming Lua when it does not work as expected.

But I decided this time to read the whole post before replying, so
into the floating-point quagmire:

for x=math.maxinteger,math.maxinteger do print(x) end

> There is a problem in numeric "for"-loop: Lua internally exceeds the range of values specified by user.
>  > The loop
>    for x = a, b, c do ... end
> is currently executed as follows:
>    y = a - c
>    while true do
>       y = y + c
>       if y <?= b then
>          set x = y and execute the loop body
>       else
>          break
>       end
>    end

What the 5.3.5 manual actually says is more complicated:

<quote>
...a for statement like

for v = e1, e2, e3 do block end

is equivalent to the code:

do
local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3)
if not (var and limit and step) then error() end
var = var - step
while true do
var = var + step
if (step >= 0 and var > limit) or (step < 0 and var < limit) then
break
end
local v = var
block
end
end
</quote>

> The range specified by user is: a <= x <= b
> Lua exceeds it in the beginning (first value of y = a-c)
> and in the end (the last value of y might be b+c).
> It might happen that a-c+c ~= a
> It might also happen that b+c == b
> User intuitively implies the correctness of addition and comparison operators for values inside the range a <= x <= b
> He might not expect that Lua performs calculations beyond this range.

He should expect that Lua does, unless he is the kind of user that

But it acrually makes matters worse that the objectionable behaviour
is not an implementation detail but part of the specification.

> Another not-a-good-thing is that Lua uses subtraction in numeric "for"-loop (the loop is supposed to always add and never subtract).
> This would prevent generalization of numeric "for"-loop (Sony L. some time ago asked for this useful feature).
> Addition and comparison metamethods should be enough to perform generalized numeric "for"-loop with arbitrary user objects.
> Such objects must implement operations "obj+obj", "obj==obj", "obj<obj" and "obj<0".
> The last one is needed to determine whether the loop is "increasing" (step>0) or "decreasing" (step<0).
>
>
> My suggestion is to implement numeric "for"-loop in more accurate way:
> - don't use subtraction;
>
>
> The loop
>    for x = a, b, c do ... end
> should be executed as follows:
>    y = a
>    while y <? b do
>       set x = y and execute the loop body
>       y = y + c
>    end
>    if y == b then
>       set x = y and execute the loop body
>    end

It may happen that y+c is undefined even though upper limit is not
reached. For this code to work, user's objects should therefore
include concept analogous to NaN.

> Lua VM instructions would be the following:
>
>  FORPREP
>  loop body
>  FORLOOP
>  next instruction after the loop
>
> R(A)   = index
> R(A+1) = limit
> R(A+2) = step
> R(A+3) = user loop variable
>
> R(A), R(A+1) and R(A+2) might contain user objects instead of Lua numbers.
> Operator ">?=" implies comparison of R(A+2) with number 0 to be resolved:
> to ">=" for increasing loop, to "<=" for decreasing loop.

> A trick is used here:
> when the index R(A) reaches the limit R(A+1), the step R(A+2) is set to nil to mark the current iteration as final.

A better trick would be to set the index to nil. Then the user can
simply return nil or nothing when R(A)+R(A+2) is not a valid object.

But all this will still not solve the problem that the following loop
does not work as expected:

> for k=1,2,1/6 do print(k) end
1.0
1.1666666666667
1.3333333333333
1.5
1.6666666666667
1.8333333333333
>

So I have an alternative suggestion.

Simply avoid using the numerical 'for' when it might get dubious.

Put all the clever tricks in a function 'loop' and use the generic for:

for v in loop(e1,e2,e3) do

```