可变函数 |
|
我们首先创建一个名为 mutable
的装饰器函数 [1],即一个返回函数的函数,该函数是传入函数的变体(包装器)
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)
我们现在拥有元方法的回退语义(例如 __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 元方法中的多个返回值"