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
[edit | edit source]Functions can be defined in the "ordinary" way, and we can call them.
>functiondouble(x)returnx*2end>returndouble(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.
>returndoublefunction:0x806a5e8>a={double}-- creates an array>returndouble==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.
>returnfunction(x)returnx*2endfunction:0x806b950>return(function(x)returnx*2end)(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
[edit | edit source]Function 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
>functiondofunction(f)returnf(21)end>returndofunction(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.
functionmap(func,array)localnew_array={}fori,vinipairs(array)donew_array[i]=func(v)endreturnnew_arrayend
This is a simple map
implementation that only works with one array. But it works well:
>returntable.concat(map(double,{1,2,3}),",")2,4,6
A more complex map
implementation that works with more than one array is possible:
functionmapn(func,...)localnew_array={}locali=1localarg_length=table.getn(arg)whiletruedolocalarg_list=map(function(arr)returnarr[i]end,arg)iftable.getn(arg_list)<arg_lengththenreturnnew_arrayendnew_array[i]=func(unpack(arg_list))i=i+1endend
And let's use it:
>t=mapn(function(a,b)returna+bend,{1,2,3},{4,5,6})>returntable.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)returna<bend)>returntable.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.
functioncdr(arr)localnew_array={}fori=2,table.getn(arr)dotable.insert(new_array,arr[i])endreturnnew_arrayendfunctioncons(car,cdr)localnew_array={car}for_,vincdrdotable.insert(new_array,v)endreturnnew_arrayendfunctionlisp_remove_if(func,arr)iftable.getn(arr)==0thenreturn{}endiffunc(arr[1])thenreturnlisp_remove_if(func,cdr(arr))elsereturncons(arr[1],lisp_remove_if(func,cdr(arr)))endend
Compare with the following Lisp code:
(defunour-remove-if(fnlst)(if(nulllst)nil(if(funcallfn(carlst))(our-remove-iffn(cdrlst))(cons(carlst)(our-remove-iffn(cdrlst))))))
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:
functionlua_remove_if(func,arr)localnew_array={}for_,vinarrdoifnotfunc(v)thentable.insert(new_array,v)endendreturnnew_arrayend
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)returnmath.mod(x,2)==0end,{1,2,3,4,5})>returntable.concat(t,",")1,3,5