I had an assignment for my Programming Language homework which makes us to experiment over the language Lua. One of the aspect of the language we required to experiment over is should the conditional mechanism be an integral part of the exit (conditional or unconditional exit)? So my friends and I discussed it as it is a question of whether the loop construction of the program needs controller or not. For example in Lua
for i = 0 , 5, 1 do
print("Something")
end
loop statement chooses conditional exit according to our discussion with friends. On the other hand, I think it is not a design choice since the generally every programming language chooses conditional exit. So why it is a design choice for the programming language? If we think wrong about design decision can you explain what is this design decision about?
I will assume you're referring to the following notes (based on a quick search):
It is sometimes convenient for a programmer to choose a location for loop control other than the top or bottom of the loop. Design issues:
- Should the conditional mechanism be an integral part of the exit?
- Should only one control body be exited, or can enclosing loops also be exited? C and C++ have unconditional unlabeled exits (
break
) Java, Perl, and C# have unconditional labeled exits (break
in Java and C#,last
in Perl) [...]
Lua offers 5 looping constructs: The classic while
loop for loops that are supposed to run zero or more times, the repeat
-until
loop (comparable to do
-while
, except the condition is inverted) for loops that run one or more times, the numeric for var = from, to, step
loop from your example for iterating over evenly spaced numbers in a range known beforehand, the very flexible generic for var1, var2, ... in iterator, invariant_state, control_var
loop, and finally, tail recursion using function f() --[[body]] return f() end
.
break
Tail recursion is the odd one out here so let's start with the other loops. Given a while
/repeat
or for
loop, you can use the break
loop control statement to exit the innermost loop. You could use this to implement for i = 1, 7 do print(i) end
as follows without using a loop condition:
local i = 1
while true do
print(i)
if i == 7 then
break
end
i = i + 1
end
(note that this is not a particularly useful example).
To exit multiple loops, you'd have to use either a goto
or a return
:
for i = 1, 10 do
for j = 1, 10 do
if frobnicate(i, j) then
goto after_loops
end
end
end
::after_loops::
...
using a return
:
(function()
for i = 1, 10 do
for j = 1, 10 do
if frobnicate(i, j) then
return
end
end
end
end)()
...
(often this would lead you to properly extract the nested loop into a named function)
Other workarounds use boolean variables stuffed into the loop conditions/iterators to achieve the same effect, but this is rather dirty (and also slightly inefficient). goto
is the cleanest solution here.
continue
Lua does not have a continue
. The simplest workaround again is to wrap the chunk you want to continue in a function and call return
:
for k1, v1 in pairs(t1) do
for k2, v2 in pairs(t2) do
(function()
if foo(k1, v2) then
return -- "continue"
end
bar(k2, v1)
foobar(k1, v1)
end)()
end
end
Caveat: You're creating an anonymous function in every loop iteration. It is again flexible enough to allow continuing multiple loops. Again, a goto
is the cleaner solution:
for k1, v1 in pairs(t1) do
for k2, v2 in pairs(t2) do
if foo(k1, v2) then
goto continue
end
bar(k2, v1)
foobar(k1, v1)
::continue::
end
end
Finally, if you use (tail-)recursive functions and want to jump out of multiple functions at once, you can use coroutine.yield
for that; in fact, coroutine.wrap
is usually used for complex (recursive) iterators, as you'd otherwise have to explicitly manage the program state (stack) iteratively rather than managing it implicitly in the call stack.
break
statement for exiting a single loop.continue
statement.goto
which can be used to the same effect as breaking/continuing of labeled loops in other programming languages. This serves to eliminate the need for continue
at slight verbosity.break
the loop, you can start the next loop iteration (function call) using return func(...)
at arbitrary places, skipping the rest of the function body. If you use upvalues, you don't even have to pass parameters.