This tutorial is now complete. Post suggestions for change in comments below.

Tutorial: Lua Basics – Variable Scope, Code Blocks and Control Structures

As a newcomer to Lua (but not to programming in general), I needed to wrap my brain around a few concepts that are specific to the Lua way of doing things. One of the first things I studied was issues of variable scope, how Lua defines blocks of code, and what control structures were available to me in Lua.

This tutorial is offered as a topic in the Ask A Pro series of tutorials requested by Lyqyd (see [post='137243']Ask A Pro Renewal Project (and How You Can Help!)[/post]). Specifically, it will introduce the concept of Global and Local Variables, Code Blocks and Control Structures in the language. Every programmer new to Lua (or every new programmer) needs to tackle these topics early on in the learning of the language. Keywords covered in this tutorial include:
  • local
  • do
  • end
  • if
  • then
  • else
  • elseif
  • while
  • repeat
  • until
  • for
Pre-RequisitesThis tutorial serves as a fairly thorough introduction to the basics of the Lua Language. However, I would assume the purpose of learning all of this is so that you can program up awesome turtles or computers in Minecraft to create minions to do your bidding. Consequently, in order to try out these examples and really get the benefit out of using this tutorial, you may want to start with [post='15033']Computer Basics 1[/post] by Lyqyd which will show you how to make a computer and how to get around the CraftOS a bit.

Variable Scope and Code BlocksEvery programming language has some practice of using code blocks. Code blocks are sections of code that execute at the same level, that is, all of the variables locally defined within the block are local to the block and when the last command in the block is executed (or when a break or return statement is encountered), the block will exit to the calling routine and the local variables will disappear and be passed to the garbage collector.

A Lua program itself is a block of code that is called (most likely) from the Lua Shell. The startup program of a computer or turtle is no different. It will run, and when it finishes (assuming it exits without error), it will return to the command shell. Any variables locally defined in the program will then fall out of scope – that is they will no longer be visible to the command shell.

Simple Examples of Code BlocksCode blocks are organization structures as well as functional structures within code. Although a fairly simple concept, code blocks can have a profound effect on programming because of the way variable scoping is implemented in a language (more on the scope of variables in the next section). The most important things to learn about code blocks is that they exist, how to recognize them, and (as you become a better programmer), how to use them to accomplish what you want to code in Lua.

The most basic example of a code block is a Lua program itself.

-- My program starts here.  It is also a Code Block
print("Hello world!")
--My program ends here as does the Code Block

Here is an example showing several different code blocks.

do
   --Code block
   print("Hello world!")
end

if a==b then
   --Code Block
   print("Hello again.")
   print("I am in a code block")
else
   --Code Block
   print("Now I am in a different code block.")
end

Local Variables and ScopeSome languages consider all variables defined in a block to be local to the block and sub-blocks and only variables defined as global to be global throughout the execution of the program no matter what block it is referenced in. Other languages consider all variables to be global unless they are defined as local, and then local variables are local to the code block they are defined within. In Lua, all variables are global unless they are defined with the local keyword.

Why do we care about local versus global variables? We care for two reasons: efficient memory management and a concept referred to as the scope of a variable (variable scoping). A variable is in scope for those blocks where it holds its value. When it falls out of scope, that particular version of the variable is no longer visible to the code.

From a technical standpoint, when a variable falls out of scope it's value is set to nil, and no further references to that version of the variable exist within the program. As a result, it is eligible for garbage collection – the process in a language which destroys variables which are no longer visible and frees up the memory allocated to their previous existence. This helps the program use memory in a more efficient manner than would otherwise be the case without garbage collection. Also, from a purely technical standpoint, the fewer variables the language has to look up at any given point in the program, the faster the language runs. Global and Local variables operate behind the scences off of different lookup tables in the language. Using local variables and reducing the number of global variables can greatly speed up a program and allow it to operate with far less memory than it would if all variables had been defined globally.

However, using local and global variables can have a dramatic impact on your code if a variable you thought was in scope in fact is not any longer in the code block you are executing. For example, consider the following code with a couple of local and global variables and several code blocks:

-- program start
x = 10				   -- this is a global variable
local i = 1			 -- local to the program
while (i <= x) do	-- Which x do you think will be tested? Local or Global?
   local x = i*2	   -- local to the while code block
   print(x)			   --> 2, 4, 6, 8, …
   i = i + 1
end
	
if i > 20 then
   local x			  -- local to the "then" code block
   x = 20
   print(x + 2)	   --> 22 (the local one)
else
   print(x)			 --> 10  (the global one)
end
	
print(x)				--> 10  (the global one)

As you look over the code, pay specific attention to what happens to the variable x at different locations in the program. At first, x is a global variable with a value of 10. In the while loop, a condition is declared that tests to check if (i &amp;lt;= x). But which x? The global x or the new local x which is declared inside of the while loop? It turns out that the condition is testing against the global x. In the while loop, the local x will end up having values of 2, 4, 6, 8 .. 18,20. Next, there is an if .. then .. else .. end section of code. In this section, the "if" condition tests whether (i &amp;gt;20). It won't be, because i = 11 at that point in the program. The lines of code between the "then" and the "else" are a new code block. In this code block, a new local x is declared and set to the value 20. IF x+2 had been printed from within this code block, the program would have displayed the local value + 2 or 22. Since the variable "i" had been less than 20, the section of code between the else and the end is executed. In that section, the x is the global x, not the local one (because there is no local x in that code block). Finally, before the program exits, we note that it is back to the main block of code which it started in. In the main block of code, the variable x is the global variable x. Therefore, the last print statement will print the global value of x which is 10. In the above code, confusion over which variable version of X is the local version versus the global version would lead to different outcomes in program results.

It may take some effort to follow all of the ins and outs of scope, but once you get it you get it. The next section will put both code blocks and the scope of variables together and provide additional examples of the scope of variables.

Code Blocks using Local and Global VariablesNow lets look deeper into code blocks and the use of variable scoping through local and global variables.

With exception of the execution section of a simple program, all code blocks begin with a keyword and end with a keyword. Beginning keywords defining code blocks include do, then, else and repeat. Ending keywords (depending on the beginning keyword) include end and until. There are two basic code blocks in lua: a program (which is considered a chunk or a code file) or a simple do / end clause. All programs are code blocks in and of themselves.

Here is a simple program:

-- This is my program (it is also a code block)
local x = 5
print (x)	   -->5
-- end of program block, will return to shell (x will fall out of scope)
The simple program is executed from the shell, which begins a new code block. Upon countering an end-of-file or some other terminating event, the simple program will exit and return back to the shell. In the program, a local variable "x" is declared and then printed. Upon exit, "x" falls out of scope and is reclaimed by the garbage collector.

Here is a slightly more complex program with a do/end block defined inside of it:

--this is my program ( a code block itself)
local x = 5
print (x)		-->5
do			   -- start of a block within the program block
  local x = 6
  print (x)	  -->6
end			  -- end of block
print x		 -->5 (the do/end x is out of scope)
-- end of program block, will return to shell (x will fall out of scope)

The more complex program demonstrates the concept of local scope more graphically. Here, we have the initial local variable x in the programs code block. We print x and, not surprisingly, we discover it has a value of 5 at that point in the code. Then, we declare a new code block with the do keyword (which is running within our original code block). In this new code block, we declare a local variable which also has the name of x. That may seem confusing to us, but Lua recognizes it as a completely separate variable with a scope limited to the new code block. So, when we print the variable we notice it has a value of 6 which is correct for the variable in this scope. Finally, we exit the new code block at the keyword end. At that point, the new local x falls out of scope and is garbage collected. But what about the original local x? It is still there because it is contained within the original code block (the program itself). We print it and find out that it has a value of 5 – all is right with the world again.

The next program demonstrating the scope of variables should clear any remaining confusion up, because we aren't going to use any confusing same-named variables.

local x = 5
do
   local y = 6
   print (x)
   print (y)
end
print (x)
print (y)

Can you guess what will happen here? We declare a local variable x in the main code block of the program. Then we declare a new code block. In the new code block, we declare the local variable y – it is a local variable to the new code block. When we print x from within our new code block, we will see a 5, because x is still in scope to the new code block. When we print y from within the new code block, we will see 6 (no surprise here). But when we end the new code block, what will happen when we try printing both variables again? We will find is that x is still in scope, but y has fallen out of scope. The program will display a 5 and then a blank line (y is nil).

Best Practices Regarding Local/Global Variables and Code Blocks

Local Variables tend to help with program performance and reduce program memory consumption, but it is very important to keep the scope of any variable in mind. A good practice is to get used to thinking of all variables as local and define them with the local keyword. Global variables should only be used when you know you need to access a variable's value outside of the code block in which it is defined. As for code blocks, most of that will be taken care of by the simple act of coding up your program. In Lua, it is unnecessary to define every block of code explicitly with a do / end pair. The syntax of most control structures will take care of that for you.

Control StructuresAt a fundamental level, all programs are sequential in nature: that is, each program will start with the first statement and execute each and every statement in the program until it encounters some sort of exit condition or the very end of the program itself. This is not extremely useful if you are attempting to loop, or you have certain sections of your code that you want to execute only if some condition is satisfied. Hence the need for control structures.

Control structures effect the flow of the program – how a program jumps from statement to statement and interrupts or avoids sequential ordering of statements. In 'Ye Olden Veritable Basic Programming Language and variances thereof, you might find goto statements or gosub statements. You would definitely find some kind of iterative for … next loop and if … then statements. Lua is a bit more advanced than this, but the general concepts of program control remain.

In Lua, there are essentially four controls structures: one purely conditional control structure and three looping (iterative) control structures:
  • if – a conditional control structure (ifthenelse or elseif/thenend)
  • while – the while loop (whiledoend)
  • repeat – the repeat loop (repeatuntil)
  • for – the for loop (fordoend)
[indent=1]Note: some purists will claim that the for loop is actually a hybrid of the do/end code block, with the for being a modifier for the do/end block. The same is argued on the while control structure and the if control structure. I really don't care to enter this debate, nor should you. The real takeaway idea here is that any section of code encapsulated within a do/end pair is a control block as discussed earlier. Thus, an if control structure could be read as if (condition) then (control block). Likewise a for control structure could be read as for (options) (control block), and a while control structure becomes while (condition) (control block). Only the repeat control structure escapes the purists scrutiny on this point, yet the fact remains that the repeat control structure is essentially a repeat (control block) until (condition).[/indent]

All of the Lua control structures have a condition involved in their execution. A condition is satisfied if the conditional statement evaluates to a boolean true. The condition remains unsatisfied if the conditional statement evaluates to a boolean false.

Examples of Conditional StatementsWhile this is by no means a primer on conditional statements, it is nevertheless useful to look at some examples of conditional statements in order to understand the concept of satisfying a condition for control structures. In the following code, various conditions are proposed and their outcomes are displayed in print statements:

-- declare some local variables
local myBooleanVariable = true
local myFirstVariable = 10
local mySecondVariable = 15

-- print the results of some Conditional Statements
print(myBooleanVariable)
print(myBooleanVariable == false)
print(myFirstVariable >= 6)
print(myFirstVariable < 12 and mySecondVariable > 10)
print(myFirstVariable < 0 or mySecondVariable > 20)

-- results:
-- true  (yes, myBooleanVariable is true)
-- false (no, myBooleanVariable is NOT false)
-- true  (myFirstVariable is 10, so it is greater than or equal to 6)
-- true  (myFirstVariable is less than 12 and mySecondVariable is greater than 10, so this is true.
-- false (neither myFirstVariable is less than 0 nor mySecondVariable is greater than 20)

The "if" Control StructureThe if control structure is purely a conditional control structure, it does not allow for looping or iteration like the other three control structures. The if control structure causes a great deal of confusion with new programmers. That is because it can be quite flexible, and comes in several "flavors." An error in syntax among the various "flavors" of the if control structure can stop new programmers in their tracks as they scratch their heads debugging (I know this is true for me).

Essentially, there are four "flavors" of the if control structure, in increasing complexity:
[b]if[/b] (condition) [b]then[/b] (code block) [b]end[/b]The basic 'if (condition) then (code block) end' control structure executes as follows: When encountered in code, the program pauses to test the condition. If the condition evaluates as true, the program will execute the code block following the then keyword. If the condition evaluates to false, the code block following the then keyword will be ignored. In either case, once the condition is evaluated and the code block is executed (or ignored), the program continues with the statement immediately following the end keyword. Here is an example:
local x =1
print("Let's test if x > 1 …")
if (x > 1) then
   print("Yup.  It is.")
end

if (x <= 1)
   print("Nope.  It ain't")
end

print("Done Testing.")

--Result:
--Let's test if x > 1 …
--Nope. It ain't.
--Done Testing.
[b]if[/b] (condition) [b]then[/b] (code block) [b]else[/b] (code block) [b]end[/b]The else keyword adds a useful wrinkle to the basic 'if (condition) then (code block) end' control structure. Allowing for an else condition speeds up program logic and simplifies coding. Essentially, the else condition triggers when the logical inverse of the if condition occurs. Using basic logic, one could read an 'if (condition) then (code block) else (code block) end' as follows:

If all dogs are blue, take a left turn when you walk your dog. However, if even one dog is not blue, take a right turn when you walk your dog. After you figure all this out, get along with the rest of the program.

The advantage of the 'else' keyword is that the programmer doesn't need to come up with the logically inverse conditional statement: 'else' does it for you. Here is an example:
local x =1
print("Let's test if x > 1 …")
if (x > 1) then
   print("Yup.  It is.")
else
   print("Nope.  It ain't")
end

print("Done Testing.")

--Result:
--Let's test if x > 1 …
--Nope. It ain't.
--Done Testing.
[b]if[/b] (condition) [b]then[/b] (code block) [b]elseif[/b] (condition) [b]then [/b](code block) [b]end[/b]An additional twist of the 'if (condition) then (code block) end' is the use of the 'elseif' keyword. Like the else keyword, the elseif condition is only processed when the logical inverse of the if condition occurs. More than just an 'else' operation, the 'elseif' combines an else with an additional if. Moreover, elseif statements can be chained one after another following an initial if statement. This is remarkably useful in programming, as it allows the programmer to test against a range of possibilities and perform different tasks depending on whatever condition first proves to be true.

While this pattern is quite useful in fleshing out the logic of your program – several points need to be remembered regarding the if .. then .. elseifthenelseifthenend chain of statements:
  • The elseif keyword is one word, not two. Unlike some languages, in Lua, there is no space in the elseif keyword.
  • Every elseif phrase must have a then following the logical condition. This is easy to forget.
  • Only ONE of the conditions will be satisfied in the entire chain, and only the code block of the first one to be evaluated as true will be executed. So, in your chain of statements, if the initial if condition is true, the code block following the then keyword of the if will be executed. If in the list of elseif conditions more than one could be true, only the code block linked to the first one to satisfy the condition will execute. All others will be ignored.
  • The final keyword in any 'if (condition) then (code block) elseif (condition) then (code block) end' chain MUST BE the 'end' keyword. This is also very easy to forget.
In the code example, there are three 'elseif' conditional statements. The third will never execute.

local x =3
print("Let's figure out what x is …")
if (x == 1) then
   print("Yup.  X is One.")
elseif (x==2) then
   print("X actually is two")
elseif (x==3) then
   print("X is three.  I am certain of it."
elseif (x = 5-2) then
  print ("This line of code should never have been executed, because the last elseif would have evaluated to true before this one.")
end

print("Done Testing.")

--Result:
--Let's figure out what x is …
--X is three.  I am certain of it.
--Done Testing.
[b]if[/b] (condition) [b]then[/b] (code block) [b]elseif[/b] (condition) [b]then [/b] (code block) [b]else[/b] (code block) [b]end[/b]The 'if (condition) then (code block) elseif (condition) then (code block) else (code block) end' control structure is identical to the 'if (condition) then (code block) elseif (condition) then (code block) end' control structure with one incredibly useful difference. Can you see it? Think of it this way: If you have a list of possibilities that you are trying to test against, what happens if none of them evaluate as true? That's where the plain old 'else' keyword comes in handy. In essence, Lua will test every condition in the if .. elseif .. elseif chain and if none evaluate to true, it will perform whatever code block follows the else keyword.

A common use of this control structure is in creating a simple user menu in your programs. You can test for each option that you have in your menu, and if none of the options are input by the user, you can print a message and repeat the process. In the code example that follows, we will use a while control structure and a char event to display a menu and obtain user input. If the user doesn't give us what we want, we will print a message to the user and reprint the menu. There are more efficient ways of doing this ( see [post='161225']Lua Basics – Using "break" to Exit Loops[/post]), but the example below is for purposes of illustrating this last if variant. Try it out.


--simple user menu
local choice = false
while (choice == false ) do
   print ("1) Walk the dog.")
   print ("2) Go to school.")
   print ("3) Play Minecraft.")
   print ("What would you like to do (1-3)?--&amp;gt;")
   local event,char = os.pullEvent("char")
   if (char=="1") then
	  print ("Remember to take plastic bags.")
	  choice=true
   elseif (char =="2") then
	  print ("Don't forget your backpack.")
	  choice=true
   elseif (char =="3") then
	  print ("NOW we're talking...")
	  choice=true
   else
	  print("Please press 1,2, or 3")
   end
end

The "while" Control StructureThe while control structure creates a potentially repeating loop. For that reason, it is an iterative control structure. The while control structure sets up a code block that will be executed while a condition is true, and the condition is tested at the beginning of the control structure. Its format is: while (condition) do (code block) end. This can be read as follows:
[indent=1]"When you encounter the while keyword, test the condition. If it is false, jump down to the statement immediately following the end keyword, and continue program execution from there. If it is true, do the statements in the control block until you encounter the end keyword (or until you encounter the break keyword**). Then, come back up here and test the while condition again. Keep going as long as the while condition remains true."[/indent]
[indent=1]**(on the break keyword, see [post='161225']Lua Basics – Using "break" to Exit Loops[/post])[/indent]
The most common mistake for beginning Lua coders is to forget to put the end keyword to designate the end of the control block (every do must have an end).

The control block of a while loop will only execute if the condition is satisfied at the beginning. If the condition is false, program execution will skip down to the statement immediately following the end keyword. This is in direct contrast to the repeat control structure explained in the next section.



-- Example of while control structure
local x =1
print("Let's play hide and seek.. I'll count to 5."
while x <= 5 do
   local ready = ""
   if x == 4 then
	  ready ="You'd better hide!!!"
   end
   print(tostring(x).."…"..ready)
   x = x + 1
end
print("Ready or not, here I come!!!")

--result:
--Let's play hide and seek.. I'll count to 5.
--1...
--2...
--3...
--4...You'd better hide!!!
--5...
--Ready or not, here I come!!!

The "repeat" Control StructureThe repeat control structure is similar to the while control structure in that it will repeat a control block under certain conditions. It differs from the while because it tests the condition at the end of the loop (following the until keyword). The while control structure tests the condition at the beginning of the loop. This means that a repeat control structure will always execute the control block at least one time.

The format of a repeat control structure: repeat (code block) until (condition).

-- Example of repeat control structure
local x =1
print("Let's play hide and seek.. I'll count to 5."
repeat
   local ready = ""
   if x == 4 then
	  ready ="You'd better hide!!!"
   end
   print(tostring(x).."…"..ready)
   x = x + 1
until x > 5
print("Ready or not, here I come!!!")

--result:
--Let's play hide and seek.. I'll count to 5.
--1...
--2...
--3...
--4...You'd better hide!!!
--5...
--Ready or not, here I come!!!

The "for" Control Structure as a Counting LoopThe for loop is an iterative loop, combining the up front test of a while loop with a counting mechanism. It basically lets you loop until a counter has reached the limit you have defined for it. The for control structure takes a starting value, an ending value and optionally a step value. The following program demonstrates the for loop with variables you can change to play around with each aspect of the for loop. Copy the program and see what happens if you change the values for startValue, endValue and the optionalStepValue.

local startValue =1
local endValue = 10
local optionalStepValue =1
for myCounter = startValue,endValue,optionalStepValue do
   print (myCounter)
end

The logic of a for loop is that the counter variable is initialized to a starting value. At that point, the condition of the for loop is tested – is the starting value less than or equal to the ending value? If the condition is true, then the code block of the for loop is executed. If not, the loop exits. Keep in mind that if at the beginning of your loop, the starting value is greater than the ending value, the loop will exit and the code block will never execute. When the code block encounters the end keyword, the counter variable is incremented by the optional step value (default = 1 if otherwise not specified), and the loop starts at the beginning with the testing of the condition. This continues until the condition becomes false or unless a break is encountered (see [post='161225']Lua Basics – Using "break" to Exit Loops[/post])

With the above in mind, here are some things to remember when using the for control structure:
  • If the start value is greater than the end value you specify, the control block will not execute (try this for yourself).
  • The counter variable is automatically defined as a local variable of the control block. If you want to access the counter variable after the control structure finishes executing, you need to declare it prior to where the control structure begins. Otherwise, when the loop ends, the counter variable will fall out of scope and no longer be accessible to your code.
  • The step value is optional, and if you not specified, it will default to 1. But you don't need to use an integer or even a positive number. You could use a step value of .5 or -1 or even another variable like in the first example above.
  • Endless loop warning – if it is impossible for the end value to ever be achieved (consider start value of 1, end value of 2 and a step value of negative 1), the loop will never exit. Again, try it for yourself and see what happens.
  • Don't forget the do and the end keywords to define your code block.
Here are three more examples of using for loops. Try them out or make your own.


print("Let's play hide and seek.. I'll count to 5."
for (x =1,5) do
   local ready = ""
   if x == 4 then
	  ready ="You'd better hide!!!"
   end
   print(tostring(x).."…"..ready)
end
print("Ready or not, here I come!!!")

--result:
--Let's play hide and seek.. I'll count to 5.
--1...
--2...
--3...
--4...You'd better hide!!!
--5...
--Ready or not, here I come!!!

Here is the same example, this time demonstrating a step – we'll do that by showing how to count by 2's.


print("Let's play hide and seek.. I'll count to 10."
for (x =2,10,2) do -- note we start at 2, go to 10 and use a step value of 2
   local ready = ""
   if x == 8 then
	  ready ="You'd better hide!!!"
   end
   print(tostring(x).."…"..ready)
end
print("Ready or not, here I come!!!")

--result:
--Let's play hide and seek.. I'll count to 10.
--2...
--4...
--6...
--8...You'd better hide!!!
--10...
--Ready or not, here I come!!!

In this final example, we count down from 5.


print("Commence count down..."
for (x =5,1,-1) do
   print(tostring(x).."…"..ready)
end
print("Blast Off!!!")

--result:
--5...
--4...
--3...
--2...
--1...
--Blast Off!!!


A Little Bit More on the 'for' Control Structure

There is another variant of the for control structure that allows you to iterate through values in a collection (a table, list, input stream, etc), but this is not a concept for Lua Basics. If you are interested in learning about using the for control structure as collection iterator, start with the For Tutorial at http://lua-users.org/wiki/ForTutorial

Further Reading and Sources

If this tutorial was helpful, let me know. If it was confusing in sections, let me know and I will rework them. The topics I covered should give a newcomer to Lua sufficient information to use the language, especially if they have experience in other languages. I hope it is also useful for brand new programmers as well.