我刚开始学习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_width
full_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。不滥用堆栈。
-:需要额外的字节码编译步骤。