Lua Functional Programming/Functions
In Lua, as in Lisp, functions are a kind of data type; you can assign them to variables and pass them around in your code.
Defining Functions
editFunctions can be defined in the "ordinary" way, and we can call them.
> function double(x) return x*2 end
> return double(1)
2
We can access the function object created simply by using its name without any parentheses. Thus we can pass the function as an argument, store it in a data structure, etc.
> return double
function: 0x806a5e8
> a = {double} -- creates an array
> return double == a[1]
true
So-called lambda expressions or anonymous inline functions can also be created in Lua. They are similar to the "ordinary" functions described above in almost every way.
> return function(x) return x*2 end
function: 0x806b950
> return (function(x) return x*2 end)(1)
2
Note that functions and other variables share the same namespace in Lua (and in most other languages), unlike Lisp. In fact, in Lua, a function is just another kind of data you can store in a variable.
Functional Arguments
editFunction objects can be passed to other functions as arguments. To invoke an argument as a function, just append the parenthesised list that you want to invoke it with. Using the double
function defined earlier, we can do things like
> function dofunction(f) return f(21) end
> return dofunction(double)
42
The "canonical" example of a function that takes another function as a parameter is map
. Unfortunately map
does not come with Lua, so we'll have to code it ourselves.
function map(func, array)
local new_array = {}
for i,v in ipairs(array) do
new_array[i] = func(v)
end
return new_array
end
This is a simple map
implementation that only works with one array. But it works well:
> return table.concat(map(double, {1,2,3}),",")
2,4,6
A more complex map
implementation that works with more than one array is possible:
function mapn(func, ...)
local new_array = {}
local i=1
local arg_length = table.getn(arg)
while true do
local arg_list = map(function(arr) return arr[i] end, arg)
if table.getn(arg_list) < arg_length then return new_array end
new_array[i] = func(unpack(arg_list))
i = i+1
end
end
And let's use it:
> t = mapn(function(a,b) return a+b end, {1,2,3}, {4,5,6})
> return table.concat(t,",")
5,7,9
Sort is built-in, though, and we can pass a sorting function if we want.
> t = {1,4,2,5,6,7,3}
> table.sort(t, function(a,b) return a<b end)
> return table.concat(t,",")
1,2,3,4,5,6,7
On Lisp provides an example of a remove-if implementation in Lisp. Remove-if is not built-in into Lua, so we might as well code a Lua implementation which is equivalent to the Lisp code below.
function cdr(arr)
local new_array = {}
for i = 2, table.getn(arr) do
table.insert(new_array, arr[i])
end
return new_array
end
function cons(car, cdr)
local new_array = {car}
for _,v in cdr do
table.insert(new_array, v)
end
return new_array
end
function lisp_remove_if(func, arr)
if table.getn(arr) == 0 then return {} end
if func(arr[1]) then
return lisp_remove_if(func, cdr(arr))
else
return cons(arr[1], lisp_remove_if(func, cdr(arr)))
end
end
Compare with the following Lisp code:
(defun our-remove-if (fn lst)
(if (null lst)
nil
(if (funcall fn (car lst))
(our-remove-if fn (cdr lst))
(cons (car lst) (our-remove-if fn (cdr lst))))))
Both the Lua and Lisp code above are tail-recursive safe (see Tail Recursion below). (Most implementations for both languages support tail-recursion optimising.) Just for comparison, here's how I would code a "pure" Lua version:
function lua_remove_if(func, arr)
local new_array = {}
for _,v in arr do
if not func(v) then table.insert(new_array, v) end
end
return new_array
end
In Lua, we have to define the helper functions cdr
and cons
which is built-in into Lisp, but using the remove_if
is quite easy:
> t = lisp_remove_if(function(x) return math.mod(x,2)==0 end, {1,2,3,4,5})
> return table.concat(t,",")
1,3,5