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.

> 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

[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

> 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