Page 1 of 1

Introduction to Lua - Part III conjunction junction, what's your function?

#1 KYA  Icon User is offline

  • g++ jameson.cpp -o beverage
  • member icon

Reputation: 3089
  • View blog
  • Posts: 19,137
  • Joined: 14-September 07

Posted 13 April 2009 - 03:12 PM

Introduction to Lua - Part III


This tutorial requires you to have a somewhat firm grasp on basic Lua concepts (variables, how to run Lua, etc...), for complete information please read Introduction to Lua and Introduction to Lua - Part II. Having basic programming experience is helpful as well.

Functions in Lua

So far we have covered what Lua is, basic variables, simple output, and looked at tables (Lua's only data structure). While Lua is a scripting language, there is no reason to approach programming in it in a top down manner. We can create functions for our subroutines to "modularize" our code better. Basic syntax is as follows:

function functionName (params)
 --code
end



A couple of important notes, you do not have to return anything and there is no type declaration to a function (like in Python). Remember that Lua is dynamically typed and the language will take care of all of it when it runs. However, it may be helpful to provide checking to ensure no run time errors are generated from an ignorant user. For example:

print(type(varName))



This will print the type (there are 8: nil, boolean, userdata, number, table, string, function, thread) of the variable passed in. The observant among you will notice that function is one of the types of variables. You can assign functions to variables and all sorts of neat things that we will get to later on in this tutorial. However, it would be prudent to go over assignments in Lua and how they differ from other languages you may be familiar with:

a, b = 5, 6 --assign

print(a)
print(b)



Output:

Quote

5
6


Not what you were expecting I'm sure. This is an example of multiple assignment. It goes from left to right, so 'a' is 5 and 'b' is 6. In multiple assignment, Lua evaluates all values and then performs the assignment, so we can do stuff like this:

x = 7
y = 8

x, y = y, x --swap

print(x)
print(y)



Output:

Quote

8
7


This is particularly handy as C/C++ programmer will note that in order to swap two values in a similar manner we'd need to create a temp value to hold the first number swapped out. This concept applies for all types, we can even do it for tables:

a[i], a[j] = a[j], a[i]  --swap table values 'i' and 'j'



We can now rewrite the bubble sort presented in Introduction to Lua, using this technique:

function bubbleSort(table)
--check to make sure the table has some items in it
	if #table < 2 then
		print("Table does not have enough data in it")
		return
	end

	for i = 1, #table do --> array start to end
		for j = 2, #table do
			if table[j] < table[j-1] then -->switch
				table[j-1], table[j] = table[j], table[j-1]	-->NEW
			end
		end
	end

	return
end

--example usage
table = {1,10,5,6,7}
for i = 1, #table do
	print(table[i])
end
bubbleSort(table)
print("\n")
for i = 1, #table do
	print(table[i])
end



Output:

Quote

1
10
5
6
7

1
5
6
7
10


While we can see that this is more efficient/cleaner, without knowing what that means, it can certainly be considered confusing, even convoluted to an extent. One last note on this before moving on, Lua will check to see if the number of variables on the left match the number of assignments on the right. If there is less on the right, 'nil' will be provided for those variables that do not have an assignment. Be explicit!

--common mistake
a,b,c = 0
print(a,b,c)	--> 0  nil nil

--should be 
a,b,c = 0,0,0
print(a,b,c)  --> 0 0 0   correct




Keep it in Your Function!

Remember that we briefly discussed that Lua has two scopes of variables: global (by default) and local (denoted by 'local' in front of the variable's name). Local variables are local to the "block" that they are in:

--local variable example
x = 10					--global
local i = 1				--local to the chunk (i.e. this file)

while i <= x do
	local x = i*2		--local to this while loop
	print(x)
	i = i + 1
end

i = 19					-->reassign for purpose of next test
if i > 20 then
	local x				-->local to the if block
	x = 20
	print(x + 2)		-->22 (if i was > 20), change values to test
else
	print(x)			-->global 'x'
end						-->'end' denotes block



Output is omitted given the similarity to the next example, see below.

This principle is the same to the counter often initialized in a for loop in C++ style languages. Once that loop ends, the variable goes out of scope. Be careful in Lua as misscoping a variable could lead to an obscure bug. This concept applies to functions as well, adding a function to the above test:

--function declaration
function output()
	local x = 55
	print(x)
end

--local variable example
x = 10					--global
local i = 1				--local to the chunk (i.e. this file)

while i <= x do
	local x = i*2		--local to this while loop
	print(x)
	i = i + 1
end

i = 19					-->reassign for purpose of next test
if i > 20 then
	local x				-->local to the if block
	x = 20
	print(x + 2)		-->22 (if i was > 20), change values to test
else
	print(x)			-->global 'x'
end						-->'end' denotes block

--function test
output()



Output:

Quote

2
4
6
8
10
12
14
16
18
20
10
55


The best advice is to be explicit. Never let other programmers guess your intentions. Leave a comment, use local, etc... This transcends all languages. It is simply good programming practice.


Possibly the Coolest Thing You Have Ever Seen

This question gets asked a lot:

Quote

How can I return multiple values from a function???


The solution to the above question can be rather intricate, depending on the language. Passing by reference, calling multiple functions, and on and on it goes. In Lua, you can return as many values as you want! It's true! Consider the max number in a table finder snippet:

function findMax(a)
	local mi = 1			--index of max value
	local m = a[mi]			--max value
	for i, val in ipairs(a) do
		if val > m then
			mi = i
			m = val
		end
	end
	return m, mi	--neat feature of lua, can return multiple values
end


--example usage
print(findMax({2,6,90,45,67,88,100}))	-->100 7, max num and index location



Output:

Quote

100 7


We returned two values from the same function! I can hear the sound of rejoicing from your computer room. Now, if that wasn't cool enough, we can use the multiple assignment feature from earlier in this tutorial to assign values for later use:

function findMax(a)
	local mi = 1			--index of max value
	local m = a[mi]			--max value
	for i, val in ipairs(a) do
		if val > m then
			mi = i
			m = val
		end
	end
	return m, mi	--neat feature of lua, can return multiple values
end


--example usage
firstVal, secondVal = findMax({2,6,90,45,67,88,100})	-->100 7, max num and index
print(firstVal.." "..secondVal)



Output is the same.

As previously discussed, that could get confusing, so put a comment or be explicit in your variable naming conventions, etc... The above (firstVal, secondVal) was enough to know which variable was assigned what. If we changed the order of return in the function, we'd have to change the assignment order. Keep that in mind. If there are not enough values returned from a function, 'nil' is provided for extraneous variables, Lua does this automatically.


Quit Playing With Your Function at the Table!

Another neat feature that you can use with function is the unkonwn or unlimited parameters. C programmers will find this syntax familiar:

--sum all the parameters together
function add(...)
	local s = 0
		for i, v in ipairs{...} do
			s = s+ v
		end

	return s
end

print(add(3,4,25,12))			-->44



Output:

Quote

44


You've already seen a standard library function utilize this concept, print(). There are several other variations on this concept, but are out of the scope of this tutorial.


A Dive Into Functional Programming

One last item on functions: functions are first class values in Lua. This means they have the same rights as variable types like numbers and strings. Functions also have lexical scoping; this entails their ability to access variables within their enclosing functions. This can get quite complicated. Here is a relatively simple example:

--impractical example
a = {p = print}			-->p refers to the print function
a.p("Hello World")		-->calling p as print
print = math.sin		-->the print function now refers to math.sin
a.p(print(1))			--> 0.841470
sin = a.p				-->sin now refers to the print function
sin(10, 20)				--> 10 20



Output:

Quote

Hello World
0.8414709848079
10 20


This is impractical because you would not want to rewrite a standard library function such as print(). (Well you may want to, but there's no reason.) I would consider it the anti reinventing the wheel syndrome. The point of the above is to illustrate, that in Lua, functions are anonymous, they do not have names. print is actually a variable that holds the function, hence our ability to reassign it. Horribly confused on that last part? I'll be covering more advanced functional programming concepts Lua has in future tutorials. Consider your whistle thoroughly whetted.

Hopefully you found this tutorial both informative and interesting. Part IV will probably contain a roundup of anything "basic" I haven't covered yet. Happy Coding!

--KYA

This post has been edited by KYA: 14 April 2009 - 10:40 PM


Is This A Good Question/Topic? 0
  • +

Page 1 of 1