[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- 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)
>
> Let's start with two simple examples:
> 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
here is a simple example that illustrates your point without treading
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
does not read manuals.
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;
> - don't make addition when upper limit is already reached.
>
>
> 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:
>
> [1] FORPREP
> [2] loop body
> [3] FORLOOP
> [4] 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.
> No addition is made after the final iteration.
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