6

im trying this in lua:

for i = 1, 10,1 do
    print(i)
    i = i+2
end

I would expect the following output:

1,4,7,10

However, it seems like i is getting not affected, so it gives me:

1,2,3,4,5,6,7,8,9,10

Can someone tell my a bit about the background concept and what is the right way to modify the counter variable?

Daveman
  • 1,075
  • 9
  • 26
  • 2
    As you've seen, changes to the loop variable are overwritten on each iteration. You'll need to use a while loop, and set up the counter and incrementing yourself. – Colonel Thirty Two Dec 16 '15 at 16:57
  • Additionally you could make your own generator and use a generic for loop, I'd give an example but I'm not sure how preferable that method would be. – warspyking Dec 17 '15 at 23:22
  • This is not strictly a duplicate in `lua`. The referenced question merely needs to decrement through a for loop which is possible via the standard syntax `for i=start,stop,step`. This question has to do with modifying the iterator from within a loop. – KyleMit Dec 15 '20 at 16:55

3 Answers3

10

As Colonel Thirty Two said, there is no way to modify a loop variable in Lua. Or rather more to the point, the loop counter in Lua is hidden from you. The variable i in your case is merely a copy of the counter's current value. So changing it does nothing; it will be overwritten by the actual hidden counter every time the loop cycles.

When you write a for loop in Lua, it always means exactly what it says. This is good, since it makes it abundantly clear when you're doing looping over a fixed sequence (whether a count or a set of data) and when you're doing something more complicated.

for is for fixed loops; if you want dynamic looping, you must use a while loop. That way, the reader of the code is aware that looping is not fixed; that it's under your control.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
4

When using a Numeric for loop, you can change the increment by the third value, in your example you set it to 1.

To see what I mean:

for i = 1,10,3 do
    print(i)
end

However this isn't always a practical solution, because often times you'll only want to modify the loop variable under specific conditions. When you wish to do this, you can use a while loop (or if you want your code to run at least once, a repeat loop):

local i = 1
while i < 10 do
    print(i)
    i = i + 1
end

Using a while loop you have full control over the condition, and any variables (be they global or upvalues).

warspyking
  • 3,045
  • 4
  • 20
  • 37
  • Additionally you could make your own generator and use a generic for loop, I'd give an example but I'm not sure how preferable that method would be. – warspyking Dec 17 '15 at 23:22
1

All answers / comments so far only suggested while loops; here's two more ways of working around this problem:


If you always have the same step size, which just isn't 1, you can explicitly give the step size as in for i =start,end,stepdo … end, e.g. for i = 1, 10, 3 do … or for i = 10, 1, -1 do …. If you need varying step sizes, that won't work.


A "problem" with while-loops is that you always have to manually increment your counter and forgetting this in a sub-branch easily leads to infinite loops. I've seen the following pattern a few times:

local diff = 0
for i = 1, n do
   i = i+diff
   if i > n then  break  end
   -- code here
   -- and to change i for the next round, do something like
   if some_condition then
       diff = diff + 1  -- skip 1 forward
   end
end

This way, you cannot forget incrementing i, and you still have the adjusted i available in your code. The deltas are also kept in a separate variable, so scanning this for bugs is relatively easy. (i autoincrements so must work, any assignment to i below the loop body's first line is an error, check whether you are/n't assigning diff, check branches, …)

nobody
  • 4,074
  • 1
  • 23
  • 33
  • "I've seen the following pattern a few times:" And how exactly is this better than a `while` loop? Oh sure, you make infinite loops impossible, but you make it much harder to *read* what the code is doing. You've got an early break, an extra-loop variable that's some kind of different, etc. The `while` loop would be much more comprehensible. – Nicol Bolas Dec 16 '15 at 20:03
  • @NicolBolas I'm not saying it's better, just that it exists and in some situations _may_ be preferable. (E.g. if you need the diff/delta anyway, the `while` version will be clumsier.) If you care more about seeing at a glance that certain code _can not_ do something rather than quickly understanding all it does, this version may also be preferable. (Check there's _no assignment to the loop counter anywhere_ below the header vs. _identifying all paths/exits_ and _checking the increment for each path_.) What's "more readable" depends on what you're doing and what you're used to. – nobody Dec 17 '15 at 00:07