Troubleshooters.Com and Code Corner Present

Litt's Lua Laboratory:
Lua Functions
(With Snippets)

Copyright (C) 2011 by Steve Litt



Debug like a Ninja

Contents

  • Introduction
  • Hello World
  • Functions are Data
  • Passing Back Multiple Return Values
  • Varargs
  • You Can Nest Functions
  • Anonymous Functions
  • Local and Global Variables
  • Closures
  • Introduction

    Subroutines in Lua are called functions, just like in C, whether or not they return a value. Arguments to Lua functions are local variables within the called function. They're passed by value -- they're duplicates of what you passed in and don't change it.Warning  Unlike most languages, a Lua functions can pass back multiple return values -- as many as you want. Unlike C, you can pass back a local variable as a function return -- no need to use malloc() on what you return.
    WARNING!

    Although arguments to Lua functions are passed by value, meaning they're duplicates, and although local variables in functions are passed by value as returns, arguments and returns of tables are a special case, because tables are ALWAYS passed and assigned by REFERENCE!

    This means that what the function does to a table argument is visible in the calling routine after the called function returns. Likewise, if you change a table obtained from a function return, that change may be visible in other places.

    Tables are passed and assigned by reference. The only two ways to copy a table are:
    1. Create a function or method to create a new table and copy every piece of data from the original to the new
    2. Using closures (discussed on another page in the Litt's Lua Laboratory subsite)
    After reading this warning, click your browser's back button to return to what you were reading.

    Hello World

    Create the following file called hello.lua and chmod it executable, then run it:
    #!/usr/bin/lua

    function hello()
    print("Hello World")
    end

    hello()
    The preceding code, as expected, creates the following output:
    slitt@mydesk:~$ ./hello.lua
    Hello World
    slitt@mydesk:~$
    Now let's throw in an argument:
    #!/usr/bin/lua

    sf = string.format
    function hello(name)
    print(sf("Hello %s!", name))
    end

    hello("Alfred Anderson")
    hello(321)
    Notice that Lua itself doesn't check the type of the argument. We passed it a string the first time and a number the second, and it took them both. Of course usually you have problems inside the function if you use a numeric to call what should have been a string or vice versa, but in this case string.format() converted the number to a string. Here's the resulting output:
    slitt@mydesk:~$ ./hello.lua
    Hello Alfred Anderson!
    Hello 321!
    slitt@mydesk:~$
    If you really want to typecheck the incoming argument, do it inside the function:
    #!/usr/bin/lua

    sf = string.format
    function hello(name)
    assert(type(name) == "string",
    "ERROR: Only strings may be passed into hello()")
    print(sf("Hello %s!", name))
    end

    hello("Alfred Anderson")
    hello(321)
    Here's the output:
    slitt@mydesk:~$ ./hello.lua
    Hello Alfred Anderson!
    /usr/bin/lua: ./hello.lua:5: ERROR: Only strings may be passed into hello()
    stack traceback:
    [C]: in function 'assert'
    ./hello.lua:5: in function 'hello'
    ./hello.lua:11: in main chunk
    [C]: ?
    slitt@mydesk:~$
    In the preceding, putting quotes around the 321 make it a string and prevent the error message.
    Now let's pass back a value as a function return:
    #!/usr/bin/lua

    sf = string.format
    function hello(name)
    assert(type(name) == "string",
    "ERROR: Only strings may be passed into hello()")
    print(sf("Hello %s!", name))
    return("Howdy " .. name)
    end

    print(hello("Alfred Anderson"))
    print(hello("321"))
    The preceding passes back the name preceded by "Howdy ", and the two calls to hello() are now in print statements so they print the return value. Here's the resulting output:
    slitt@mydesk:~$ ./hello.lua
    Hello Alfred Anderson!
    Howdy Alfred Anderson
    Hello 321!
    Howdy 321
    slitt@mydesk:~$

    Functions are Data

    Remember the first Hello World from the hello world article:
    function hello()
    print("Hello World")
    end
    The preceding syntax is just a Lua syntax shortcut (commonly known as "syntactic sugar") for the more general syntax of assigning an unnamed function to a variable:
    hello = function()
    print("Hello World")
    end
    Or the one with arguments:
    hello = function(name)
    print(sf("Hello %s!", name))
    end
    Functions are just data that can get assigned. See this:
    #!/usr/bin/lua
    function hello ()
    print("Hello World")
    end

    hi = hello
    hello()
    hi()
    All you've done is assigned the exact same function to a second name, so the preceding code should simply print out "Hello World" twice. And indeed, when we run it, we get the following:
    slitt@mydesk:~$ ./hello.lua
    Hello World
    Hello World
    slitt@mydesk:~$
    Perhaps you've noticed that in a lot of the programs in Litt's Lua Laboratory I start with the following:
    sf=string.format
    That's to save line length and keystrokes. Instead of having to type out string.format over and over again, letting lines become longer and longer, I just type out sf. See the following:
    #!/usr/bin/lua
    sf=string.format
    print(sf("%s %s picked %d pickled peppers","Peter", "Piper", 5))
    Producing:
    slitt@mydesk:~$ ./hello.lua
    Peter Piper picked 5 pickled peppers
    slitt@mydesk:~$
    But functions-as-data is a lot more powerful than name slight of hand. Callback functions become trivial in Lua. Things like event driven programming become much easier. Combine this with OOP and you have some serious power.

    In computer-science-speak, Lua supports "first-class functions" which means approximately:
    You can read a more exact definition on Wikipedia at http://en.wikipedia.org/wiki/First-class_function, but to me the real definition is RAW PROGRAMMING POWER! Once you get used to assigning functions, passing functions as args and returns, storing functions, swapping out functions for other functions, creating functions on the fly, using callback functions, and once you start really taking advantage of these capabilities, the world is your oyster.

    Passing Back Multiple Return Values

    You can pass back multiple return values using a comma delimited list with the return statement. Here's a practical example that takes a key/value string with an equal sign as an argument and returns both the key and the value:
    #!/usr/bin/lua

    sf = string.format

    function kvsplit(strng)
    local key = string.match(strng, "^.-=")
    key = string.sub(key, 1, -2)
    key = string.match(key, "%S.*%S")
    value = string.match(strng, "=.*%S")
    value = string.sub(value, 2)
    value = string.match(value, "%S.*")
    return key, value
    end

    local key, value = kvsplit(" name = Steve Litt ")
    print(sf("Key=>%s<, value=>%s<.", key, value ))

    local key, value = kvsplit("job=troubleshooter")
    print(sf("Key=>%s<, value=>%s<.", key, value ))
    So notice that on the call to kvsplit(), the left side has a comma delimited list of variables to be filled. Here's the output:
    slitt@mydesk:~$ ./hello.lua
    Key=>name<, value=>Steve Litt<.
    Key=>job<, value=>troubleshooter<.
    slitt@mydesk:~$
    It's just as expected.

    Now what if you have too few variables on the left side? In that case, the first var on the left side is filled with the first return, etc.

    What if there are too many variables on the left side? In that case the first left hand vars are filled with the returns, and the extras are nil.

    Similar with arguments. Say the function is declared with two arguments. If you pass it no arguments, inside the function both args are nil. Pass it one and the first one has a value and the second one is nil. Pass it two and they both have values. Pass it three and the third one is ignored.

    Varargs

    Lua allows a function to take a variable number of arguments and deal with them. This is done with a special symbol consisting of three dots, which represents a variable number of arguments. C has the same capability. The three dots can either be iterated through using a special table called arg, or they can be passed to another function. This will be shown in this article. In Lua there are some fairly strict restrictions on what you can do with varargs.
    #!/usr/bin/lua 

    sf=string.format
    function printargs(...)
    for i=1, #arg do
    print(sf("i=>%s<, v=>%s<", tostring(i), tostring(arg[i])))
    end
    print()
    end

    printargs("one")
    printargs("one","two")
    printargs("one","two", "three")
    Here the special table arg contains everything passed in through ... and can iterate through it.

    The result looks like this:
    slitt@mydesk:~$ ./test.lua
    i=>1<, v=>one<

    i=>1<, v=>one<
    i=>2<, v=>two<

    i=>1<, v=>one<
    i=>2<, v=>two<
    i=>3<, v=>three<

    slitt@mydesk:~$
    You can also have named arguments before the varargs:
    #!/usr/bin/lua 

    sf=string.format
    function printargs(namedarg, ...)
    print(namedarg)
    for i=1, #arg do
    print(sf("i=>%s<, v=>%s<", tostring(i), tostring(arg[i])))
    end
    print()
    end

    printargs("one")
    printargs("one","two")
    printargs("one","two", "three")
    The preceding code should print the first argument alone on its line, and then any and all other arguments in strings. That's indeed what happens:
    slitt@mydesk:~$ ./test.lua
    one

    one
    i=>1<, v=>two<

    one
    i=>1<, v=>two<
    i=>2<, v=>three<

    slitt@mydesk:~$
    You can also pass varargs on to other functions. Consider this:
    #!/usr/bin/lua 

    sf=string.format
    function printargs(...)
    for i=1, #arg do
    print(sf("In printargs, i=>%s<, arg=>%s<",i, arg[i]))
    end
    end

    function passargs(namedarg, ...)
    print(sf("In passargs, namedarg=>%s<",namedarg))
    printargs(...)
    print()
    end

    passargs("one")
    passargs("one","two")
    passargs("one","two", "three")
    So passargs() is printing the first (name) argument and then passing the varargs to printargs(), which loops through printing them. The resulting output follows:
    slitt@mydesk:~$ ./test.lua
    In passargs, namedarg=>one<

    In passargs, namedarg=>one<
    In printargs, i=>1<, arg=>two<

    In passargs, namedarg=>one<
    In printargs, i=>1<, arg=>two<
    In printargs, i=>2<, arg=>three<

    slitt@mydesk:~$

    You Can Nest Functions

    You can nest functions like the following contrived example:
    #!/usr/bin/lua 

    sf=string.format
    function showstringreverse(str)
    function reverse(s)
    return string.reverse(s)
    end
    print(reverse(str))
    end

    showstringreverse("abcde")
    So function showstringreverse() called function reverse(), which was nested in showstringreverse(). The output is just what you'd expect.
    slitt@mydesk:~$ ./test.lua
    edcba
    slitt@mydesk:~$
    In this case there was little advantage to nesting the function as opposed to putting it above. But occasionally you might have a function so specific to another that nesting makes sense. And of course nesting functions is how you get closures, which is covered on another page of Litt's Lua Laboratory. Closures are extremely powerful.

    Anonymous Functions

    Anonymous functions are functions with no name. They're usually passed as arguments to other functions or passed back as function returns. When passed as arguments, anything longer than the most trivial function can trash readability so it's often better to name the function and then pass the name. But when returning functions making them anonymous doesn't harm readability.

    Anonymous functions can also be used as values in a table, like this:
    #!/usr/bin/lua 

    sf=string.format
    point = {x=4, y=6}
    point.show = function(self)
    print(sf("x=%d, y=%d",self.x,self.y))
    end
    point:show()
    The output is:
    slitt@mydesk:~$ ./test.lua
    x=4, y=6
    slitt@mydesk:~$
    Because we haven't covered tables yet this might not make a lot of sense to you yet, but it's an example of storing an anonymous function as point.show.

    Anonymous functions are often used as callback routines, that is, given as arguments to other, prebuilt functions in order to influence the behavior of those prebuilt functions. The classic example is passing an anonymous function, telling how to order two elements, to the table.sort() function. Did I mention earlier that callback functions give you extreme power, especially in OOP and event driven programming?

    Local and Global Variables

    This is global
    myvar = 5
    These are local
    local myvar = 5
    local myother
    myother = 5
    My opinion is every non-function variable should be local unless you have an incredibly good reason to make it global. Global variables inevitably cause problems.

    A local variable declared in a function goes is in scope only in that function. Sometimes its scope is even more limited if it's declared in a loop, branch, or do - end combo.

    Variables used as args in the declaration of a function are local to that function -- no need to declare them local above the function. Variables used in general or numeric for loops are local to the loop, and need not be declared above the for loop. Speaking of for loops, if you need the value of a loop control variable after the loop finishes, you'll need to set a wider scope variable equal to the loop control variable from within the loop.

    Closures

    We've barely scratched the surface of functions. Later, on the page devoted to Lua Closures, you'll  see functions walk and talk.


     [ Troubleshooters.com| Code Corner | Email Steve Litt ]

    Copyright (C) 2011 by Steve Litt --Legal