分享

loops

 quasiceo 2014-01-16

I have been dealing a lot with Lua in the past few months, and I really like most of the features but I'm still missing something among those:

  • Why is there no continue?
  • What workarounds are there for it?
asked Aug 19 '10 at 18:24
Dant
141123

2  
Since this question was asked, Lua got a goto statement which can be used to implement continue. See the answers below. –  lhf Dec 11 '12 at 17:25
add comment

7 Answers

The way that the language manages lexical scope creates issues with including both goto and continue. For example,

local a=0
repeat 
    if f() then
        a=1 --change outer a
    end
    local a=f() -- inner a
until a==0 -- test inner a

The declaration of local a inside the loop body masks the outer variable named a, and the scope of that local extends across the condition of the until statement so the condition is testing the innermost a.

If continue existed, it would have to be restricted semantically to be only valid after all of the variables used in the condition have come into scope. This is a difficult condition to document to the user and enforce in the compiler. Various proposals around this issue have been discussed, including the simple answer of disallowing continue with the repeat ... until style of loop. So far, none have had a sufficiently compelling use case to get them included in the language.

The work around is generally to invert the condition that would cause a continue to be executed, and collect the rest of the loop body under that condition. So, the following loop

-- not valid Lua 5.1 (or 5.2)
for k,v in pairs(t) do
  if isstring(k) then continue end
  -- do something to t[k] when k is not a string
end

could be written

-- valid Lua 5.1 (or 5.2)
for k,v in pairs(t) do
  if not isstring(k) then 
    -- do something to t[k] when k is not a string
  end
end

It is clear enough, and usually not a burden unless you have a series of elaborate culls that control the loop operation.

answered Aug 19 '10 at 22:58
RBerteig
21.4k3778


 
Coming from a python background this is a confusing answer because every scope there already knows what are its local variables before running. I.e. I expected an unbound local variable error in the case of reaching until.... –  ubershmekel Nov 1 '12 at 10:55

 
I know next to nothing about Python. Lua takes a very narrow view of scope for local variables: it is controlled lexically. The scope is limited at the end of the block containing the variable. The quirk is that the condition of the unless clause is inside the block lexically, because it makes the usual cases clearest. The example above with a local variable a shadowing a global variable a shows a case where that invisible lexical boundary can be confusing. Lua 5.2 introduces goto, but requires that it not be used in a way that would break lexical scoping. –  RBerteig Nov 1 '12 at 18:00

 
I think you make a fair point, but when you state "If continue existed, it would have to be restricted semantically to be only valid after all of the variables used in the condition have come into scope". Could you explain why this must be enforced? I don't see why a lookup on "inner a" simply could not result in null as any other "non-defined" symbol in the current scope (which is what I expected). –  udoprog Nov 27 '12 at 4:01

 
There was a lot of discussion of this in the Lua community before the introduction of goto into Lua 5.2. Naturally, goto has the same issue. They eventually decided that whatever the runtime and/or code generation costs were to protect against it were worth the benefits of having a flexible goto that can be used to emulate both continue and multi-level break. You'd have to search the Lua list archives for the relevant threads to get the details. Since they did introduce goto, it obviously was not insurmountable. –  RBerteig Nov 28 '12 at 20:22
add comment

In Lua 5.2 the best workaround is to use goto:

-- prints odd numbers in [|1,10|]
for i=1,10 do
  if i % 2 == 0 then goto continue end
  print(i)
  ::continue::
end

This is supported in LuaJIT since version 2.0.1

answered Oct 17 '12 at 7:58
catwell
2,545710


 
I hope they include an actual continue one day. The goto replacement doesn't look very nice and needs more lines. Also, wouldn't that create trouble if you had more than one loop doing this in one function, both with ::continue::? Making up a name per loop doesn't sound like a decent thing to do. –  Jonas Thiem Jan 8 at 23:33
add comment

The first part is answered in the FAQ as slain pointed out.

As for a workaround, you can wrap the body of the loop in a function and return early from that, e.g.

-- Print the odd numbers from 1 to 99
for a = 1, 99 do
  (function()
    if a % 2 == 0 then
      return
    end
    print(a)
  end)()
end

Or if you want both break and continue functionality, have the local function perform the test, e.g.

local a = 1
while (function()
  if a > 99 then
    return false; -- break
  end
  if a % 2 == 0 then
    return true; -- continue
  end
  print(a)
  return true; -- continue
end)() do
  a = a + 1
end
answered May 25 '11 at 17:10
finnw
22.6k564130


 
Please don't. You create closure environment on each iteration and this is HUGE waste of memory and GC cycles. –  Oleg V. Volkov Dec 11 '12 at 17:22

 
@OlegV.Volkov: premature optimization... –  finnw Dec 11 '12 at 18:37

 
go check collectgarbage("count") even after your simple 100 tries and then we'll talk. Such "premature" optimization saved one highload project from rebooting every minute last week. –  Oleg V. Volkov Dec 12 '12 at 10:26

 
@OlegV.Volkov while this example does put a relatively high load on the GC, it does not leak - All the temporary closures will be collected. I don't know about your project but IME most repeating reboots are due to leaks. –  finnw Dec 12 '12 at 13:22
add comment

Straight from the designer of Lua himself:

Our main concern with "continue" is that there are several other control structures that (in our view) are more or less as important as "continue" and may even replace it. (E.g., break with labels [as in Java] or even a more generic goto.) "continue" does not seem more special than other control-structure mechanisms, except that it is present in more languages. (Perl actually has two "continue" statements, "next" and "redo". Both are useful.)

answered May 24 '11 at 17:02
Stuart P. Bentley
2,21611130

add comment

You can wrap loop body in additional repeat until true and then use do break end inside for effect of continue. Naturally, you'll need to set up additional flags if you also intend to really break out of loop as well.

This will loop 5 times, printing 1, 2, and 3 each time.

for idx = 1, 5 do
    repeat
        print(1)
        print(2)
        print(3)
        do break end -- goes to next iteration of for
        print(4)
        print(5)
    until true
end

This construction even translates to literal one opcode JMP in Lua bytecode!

$ luac -l continue.lua 

main <continue.lua:0,0> (22 instructions, 88 bytes at 0x23c9530)
0+ params, 6 slots, 0 upvalues, 4 locals, 6 constants, 0 functions
    1   [1] LOADK       0 -1    ; 1
    2   [1] LOADK       1 -2    ; 3
    3   [1] LOADK       2 -1    ; 1
    4   [1] FORPREP     0 16    ; to 21
    5   [3] GETGLOBAL   4 -3    ; print
    6   [3] LOADK       5 -1    ; 1
    7   [3] CALL        4 2 1
    8   [4] GETGLOBAL   4 -3    ; print
    9   [4] LOADK       5 -4    ; 2
    10  [4] CALL        4 2 1
    11  [5] GETGLOBAL   4 -3    ; print
    12  [5] LOADK       5 -2    ; 3
    13  [5] CALL        4 2 1
    14  [6] JMP         6   ; to 21 -- Here it is! If you remove do break end from code, result will only differ by this single line.
    15  [7] GETGLOBAL   4 -3    ; print
    16  [7] LOADK       5 -5    ; 4
    17  [7] CALL        4 2 1
    18  [8] GETGLOBAL   4 -3    ; print
    19  [8] LOADK       5 -6    ; 5
    20  [8] CALL        4 2 1
    21  [1] FORLOOP     0 -17   ; to 5
    22  [10]    RETURN      0 1
answered Dec 11 '12 at 17:20
Oleg V. Volkov
8,92411132

add comment

I've never used Lua before, but I Googled it and came up with this:

http://www./

Check question 1.26. I would conjecture it's also for the same reason that goto is hated in C.

answered Aug 19 '10 at 18:39
slain
391

add comment

Again with the inverting, you could simply use the following code:

for k,v in pairs(t) do
  if not isstring(k) then 
    -- do something to t[k] when k is not a string
end

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多