可變函式 |
|
我們首先建立一個稱為 mutable
的 裝飾器 函式 ,亦即,一個會傳回函式的函式,且該函式是傳入函式的變形(包裝器)
function mutable(func) local currentfunc = func local function mutate(func, newfunc) local lastfunc = currentfunc currentfunc = function(...) return newfunc(lastfunc, ...) end end local wrapper = function(...) return currentfunc(...) end return wrapper, mutate end
在這裡,函式包裝器提供一個原函式(currentfunc)的中介層,並允許作為上值的 currentfunc 識別碼進行變異。在變異期間,我們允許取代另一個函式的函式知道自己所取代函式的識別碼,從而允許連鎖效果,其中一個函式會覆寫或篩選前一個函式的行為。
範例用法
local sqrt, mutate = mutable(math.sqrt) assert(sqrt(4) == 2) assert(sqrt(-4) ~= sqrt(-4)) -- NaN mutate(sqrt, function(old, x) return x < 0 and old(-x) .. "i" or old(x) end) assert(sqrt(4) == 2) assert(sqrt(-4) == "2i")
相對於使用以下方式,進行上述動作可能用處不大
local function sqrt(x) return x < 0 and math.sqrt(-x) .. "i" or math.sqrt(x) end
但以下是如何使用函式來模擬表格語意
local t, mutate = mutable(function() end) mutate(t, function(old, x) if x == 1 then return "first" else return old(x) end end) mutate(t, function(old, x) if x == 2 then return "second" else return old(x) end end) mutate(t, function(old, x) if x == 3 then return "third" else return old(x) end end) assert(t(1) == "first" and t(2) == "second" and t(3) == "third")
設定語法和效率當然有其不足,但我們獲得了更通用的語意
local t, mutate = mutable(function() end) mutate(t, function(old, x,y) if x == 1 then return "first" else return old(x,y) end end) mutate(t, function(old, x,y) if x == 2 then return "second" else return old(x,y) end end) mutate(t, function(old, x,y) if x > 2 then return "large number" else return old(x,y) end end) mutate(t, function(old, x,y) if y ~= 0 then return "off axis", math.sqrt(x^2+y^2) else return old(x,y) end end) assert(t(1,0) == "first" and t(2,0) == "second" and t(5,0) == "large number" and t(3,4) == "off axis") assert(select(2, t(3,4)) == 5)
我們現在具有後備的 metamethods 語意(例如 __index
),以及索引和傳回多個值的技能,後者是我們之前在 Lua 表格中所沒有的。
讓我們使用包裝 mutate
的幾個輔助函式來整理語法
local SET = function() end -- unique key local MUTATE = function() end -- unique key -- decorator function for adding methods. function mutable_helpers(func, mutate) mutate(func, function(old_func, ...) if select(1, ...) == SET then local k = select(2, ...) local v = select(3, ...) mutate(func, function(old_func, ...) if select(1, ...) == k then return v else return old_func(...) end end) else return old_func(...) end end) mutate(func, function(old_func, ...) if select(1, ...) == MUTATE then local new_func = select(2, ...) mutate(func, function(old_func, ...) return new_func(old_func, ...) end) else return old_func(...) end end) return func end
mutable_helpers
是一個裝飾函式,可新增對可變函式表示物件的方法呼叫的語意支援。這些方法是 SET
(用於設定表格值)和 MUTATE
(mutate
函式的變更語法)。SET
和 MUTATE
是識別方法的唯一鍵。這些方法利用了函式為物件的事實,函式具有唯一的識別碼(在特殊情況下,字串可替代為鍵 - 例如 "set"
和 "mutate"
)。
因此,我們現在可以在訊息傳遞形式的方法呼叫中使用類似的方法來存取模擬表格
local t = mutable_helpers(mutable(function() end)) t(MUTATE, function(old, ...) local x = select(1, ...) if type(x) == "number" and x > 2 then return "large" else return old(...) end end) t(SET, 1, "first") t(SET, 2, "second") assert(t(1) == "first", t(2) == "second", t(5) == "large")
我們可以選擇性修改函式的預設元表格以使用一般的 Lua 表格語法。(這是一個會使用真實的 Lua 表格的唯一時機,但它僅是 Lua 元機制用於支援表格語法的一個成品,而透過對 Lua 執行一些修補,就可以避免它。)
-- Enable table get/set syntax. -- Warning: uses debug interface function enable_table_access() local mt = { __index = function(t,k) return t(k) end, __newindex = function(t,k,v) return t(SET, k, v) end, } debug.setmetatable(function() end, mt) end
表格建構函式的輔助函式也將被定義
function T() return mutable_helpers(mutable(function() end)) end
範例用法
local t = T() t[1] = "first" t[2] = "second" t[3] = "third" assert(t[1] == "first" and t[2] == "second" and t[3] == "third" and t[4] == nil)
因此,在表達式方面,這表示表格不是一個 Lua 必要的功能,因此我們很可能能從語言中完全移除它們,儘管我們或許不會想因為效率考量而這麼做。
更實際的是,這或許說明了表和函數的概念可在程式語言中進一步統一,雖然為了效率,需要保留底層實作中的區別。
-- setting properties on an object obj.color = "blue" obj["color"] = "blue" obj:size(10,20,30) -- traditional syntax, method call style obj("size") = (10,20,30) -- multivalued setter syntax, function style obj["size"] = (10,20,30) -- multivalued setter syntax, table style obj.size = (10,20,30) -- multivalued setter syntex, table property style x,y = obj("position") -- multivalued getter syntax, function style x,y = obj["position"] -- multivalued getter syntax, table style x,y = obj.position -- multivalued getter syntex, table property style obj[10,20] = 2 -- multivalued keys, table style obj(10,20) = 2 -- multivalued keys, function style
相關討論:LuaList:2007-07/msg00177.html - "__index 元方法中有多個回傳值"