Jump to content

Lua Functional Programming/Functions

From Wikibooks, open books for an open world

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
close