跳轉至內容

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
華夏公益教科書