跳转至内容

Lua 函数式编程/函数

来自维基教科书,开放的书籍,开放的世界

在 Lua 中,与 Lisp 一样,函数是一种数据类型;您可以将它们分配给变量并在代码中传递它们。

定义函数

[编辑 | 编辑源代码]

函数可以用“普通”的方式定义,我们也可以调用它们。

> function double(x) return x*2 end
> return double(1)
2

我们可以通过简单地使用其名称(不带括号)来访问创建的函数对象。因此,我们可以将函数作为参数传递,将其存储在数据结构中,等等。

> return double
function: 0x806a5e8
> a = {double} -- creates an array
> return double == a[1]
true

Lua 中也可以创建所谓的 lambda 表达式或匿名内联函数。它们在几乎所有方面都类似于上面描述的“普通”函数。

> return function(x) return x*2 end
function: 0x806b950
> return (function(x) return x*2 end)(1)
2

请注意,与 Lisp(以及大多数其他语言)不同,函数和其他变量在 Lua 中共享同一个命名空间。事实上,在 Lua 中,函数只是另一种可以存储在变量中的数据。

函数参数

[编辑 | 编辑源代码]

函数对象可以作为参数传递给其他函数。要将参数作为函数调用,只需在后面附加您想要调用的参数列表即可。使用前面定义的 `double` 函数,我们可以执行以下操作

> function dofunction(f) return f(21) end
> return dofunction(double)
42

接受另一个函数作为参数的“典型”函数示例是 `map`。不幸的是,`map` 不包含在 Lua 中,因此我们必须自己编写代码。

function map(func, array)
  local new_array = {}
  for i,v in ipairs(array) do
    new_array[i] = func(v)
  end
  return new_array
end

这是一个简单的 `map` 实现,它只适用于一个数组。但它效果很好

> return table.concat(map(double, {1,2,3}),",")
2,4,6

一个更复杂的 `map` 实现,它适用于多个数组是可能的

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

让我们使用它

> t = mapn(function(a,b) return a+b end, {1,2,3}, {4,5,6})
> return table.concat(t,",")
5,7,9

排序是内置的,但是,如果需要,我们可以传递一个排序函数。

> 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 提供了 Lisp 中 `remove-if` 实现的示例。`remove-if` 不是 Lua 的内置函数,因此我们不妨编写一个等效于下面 Lisp 代码的 Lua 实现。

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

与以下 Lisp 代码比较

(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))))))

上面的 Lua 和 Lisp 代码都是尾递归安全的(见下面的尾递归)。(两种语言的大多数实现都支持尾递归优化。)仅供比较,以下是我会编写“纯”Lua 版本的方式

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

在 Lua 中,我们必须定义辅助函数 `cdr` 和 `cons`,它们是 Lisp 的内置函数,但使用 `remove_if` 非常容易

> 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
华夏公益教科书