我刚开始学习lua,所以我的要求可能是不可能的。
现在,我有一个接受函数的方法:
function adjust_focused_window(fn)
local win = window.focusedwindow()
local winframe = win:frame()
local screenrect = win:screen():frame()
local f, s = fn(winframe, screenrect)
win:setframe(f)
end
Run Code Online (Sandbox Code Playgroud)
我有几个接受这些框架和矩形的函数(仅显示一个):
function full_height(winframe, screenrect)
print ("called full_height for " .. tostring(winframe))
local f = {
x = winframe.x,
y = screenrect.y,
w = winframe.w,
h = screenrect.h,
}
return f, screenrect
end
Run Code Online (Sandbox Code Playgroud)
然后,我可以执行以下操作:
hotkey.bind(scmdalt, '-', function() adjust_focused_window(full_width) end)
Run Code Online (Sandbox Code Playgroud)
现在,我如何adjust_focused_window在不更改其定义的情况下将多个函数组合为 。就像是:
hotkey.bind(scmdalt, '=', function() adjust_focused_window(compose(full_width, full_height)) end)
Run Code Online (Sandbox Code Playgroud)
where将返回一个接受与和compose2相同参数的函数,并在内部执行类似以下操作:full_widthfull_height
full_height(full_width(...))
Run Code Online (Sandbox Code Playgroud)
正如评论中提到的,要将两个函数链接在一起,您可以这样做:
function compose(f1, f2)
return function(...) return f1(f2(...)) end
end
Run Code Online (Sandbox Code Playgroud)
但是如果您想将 2 个以上的函数连接在一起怎么办?您可能会问,是否可以将任意数量的函数“组合”在一起?
答案是肯定的——下面我展示了实现这一点的 3 种不同方法以及对其后果的快速总结。
这里的想法是依次调用列表中的每个函数。执行此操作时,您将上次调用返回的结果保存到一个表中,然后解压该表并将其传递到下一个调用中。
function compose1(...)
local fnchain = check_functions {...}
return function(...)
local args = {...}
for _, fn in ipairs(fnchain) do
args = {fn(unpack(args))}
end
return unpack(args)
end
end
Run Code Online (Sandbox Code Playgroud)
上面的助手check_functions只是检查传入的内容是否确实是函数 - 如果不是,则会引发错误。为简洁起见,省略了实现。
+:相当直接的方法。可能是您第一次尝试时想到的。
-:资源效率不高。有很多垃圾表来存储调用之间的结果。您还必须处理打包和解包结果。
这里的关键见解是,即使我们调用的函数不是递归的,也可以通过在递归函数上搭载它来使其递归。
function compose2(...)
local fnchain = check_functions {...}
local function recurse(i, ...)
if i == #fnchain then return fnchain[i](...) end
return recurse(i + 1, fnchain[i](...))
end
return function(...) return recurse(1, ...) end
end
Run Code Online (Sandbox Code Playgroud)
+:不会像上面那样创建额外的临时表。精心编写为尾递归——这意味着调用长函数链不需要额外的堆栈空间。它有一定的优雅。
通过最后一种方法,您使用一个 lua 函数,该函数实际上生成执行所需函数调用链的精确 lua 代码。
function compose3(...)
local luacode =
[[
return function(%s)
return function(...)
return %s
end
end
]]
local paramtable = {}
local fcount = select('#', ...)
for i = 1, fcount do
table.insert(paramtable, "P" .. i)
end
local paramcode = table.concat(paramtable, ",")
local callcode = table.concat(paramtable, "(") ..
"(...)" .. string.rep(')', fcount - 1)
luacode = luacode:format(paramcode, callcode)
return loadstring(luacode)()(...)
end
Run Code Online (Sandbox Code Playgroud)
可能loadstring(luacode)()(...)需要一些解释。这里我选择P1, P2, P3在生成的脚本中将函数链编码为参数名称(等)。额外的()括号用于“展开”嵌套函数,因此最内部的函数就是返回的函数。这些P1, P2, P3 ... Pn参数成为链中每个函数捕获的上值,例如。
function(...)
return P1(P2(P3(...)))
end
Run Code Online (Sandbox Code Playgroud)
请注意,您也可以使用以下方法来完成此操作setfenv,但我选择此路线只是为了避免 lua 5.1 和 5.2 之间关于如何设置函数环境的重大更改。
+:避免额外的中间表,如方法#2。不滥用堆栈。
-:需要额外的字节码编译步骤。