我刚刚开始学习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我有几个函数可以接受这些框架和矩形(只显示一个):
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然后,我可以执行以下操作:
hotkey.bind(scmdalt, '-', function() adjust_focused_window(full_width) end)现在,我如何组合几个函数来adjust_focused_window,而不改变它的定义。类似于:
hotkey.bind(scmdalt, '=', function() adjust_focused_window(compose(full_width, full_height)) end)在哪里
compose2将返回一个函数,该函数接受与full_width和full_height,并在内部执行类似以下操作:
full_height(full_width(...))发布于 2015-01-13 11:54:18
正如评论中提到的,要将两个函数链接在一起,您只需执行以下操作:
function compose(f1, f2)
return function(...) return f1(f2(...)) end
end但是如果你想把两个以上的函数连接在一起呢?你可能会问,有没有可能将任意数量的函数“组合”在一起?
答案肯定是肯定的--下面我展示了实现这一点的三种不同方法,并快速总结了它们的后果。
迭代表法
这里的想法是依次调用列表中的每个函数。在执行此操作时,将上一次调用的返回结果保存到一个表中,然后解压缩该表并将其传递给下一次调用。
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
endThe check_functions上面的helper只是检查传入的东西是否真的是函数--如果不是,就抛出一个错误。为简洁起见,省略了实现。
+:相当直截了当的方法。可能是你第一次尝试就能想到的。
-:对资源的效率不是很高。调用之间有很多垃圾表来存储结果。您还必须处理打包和解包结果。
Y-组合器模式
这里的关键洞察是,即使我们调用的函数不是递归的,也可以通过将其打包到递归函数中来使其递归。
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+:不会像上面那样创建额外的临时表。精心编写为尾递归--这意味着调用长函数链不需要额外的堆栈空间。它有一种优雅的感觉。
元脚本生成
在最后一种方法中,您将使用一个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)()(...)
endThe loadstring(luacode)()(...)可能需要一些解释。在这里,我选择将函数链编码为参数名(P1, P2, P3等)在生成的脚本中。额外的
()括号是用来“解包”嵌套函数的,所以最里面的函数就是返回的函数。The The P1, P2, P3 ... Pn参数成为链中每个函数的捕获上限值,例如。
function(...)
return P1(P2(P3(...)))
end注意,您也可以使用setfenv但我选择这条路线只是为了避免Lua5.1和5.2在如何设置函数环境方面的重大变化。
+:避免像方法2那样的额外中间表。不滥用堆栈。
-:需要额外的字节码编译步骤。
发布于 2021-02-25 12:24:21
您可以遍历传递的函数,使用前一个函数的结果连续调用链中的下一个函数。
function module._compose(...)
local n = select('#', ...)
local args = { n = n, ... }
local currFn = nil
for _, nextFn in ipairs(args) do
if type(nextFn) == 'function' then
if currFn == nil then
currFn = nextFn
else
currFn = (function(prev, next)
return function(...)
return next(prev(...))
end
end)(currFn, nextFn)
end
end
end
return currFn
end请注意立即调用的函数Expressions它允许重用的函数变量不会调用无限递归循环,这在以下代码中发生:
function module._compose(...)
local n = select('#', ...)
local args = { n = n, ... }
local currFn = nil
for _, nextFn in ipairs(args) do
if type(nextFn) == 'function' then
if currFn == nil then
currFn = nextFn
else
currFn = function(...)
return nextFn(currFn(...)) -- this will loop forever, due to closure
end
end
end
end
return currFn
end虽然Lua不支持三元运算符,short-circuit evaluation可以用来移除内部的
if声明:
function module.compose(...)
local n = select('#', ...)
local args = { n = n, ... }
local currFn = nil
for _, nextFn in ipairs(args) do
if type(nextFn) == 'function' then
currFn = currFn and (function(prev, next)
return function(...)
return next(prev(...))
end
end)(currFn, nextFn) or nextFn
end
end
return currFn
endhttps://stackoverflow.com/questions/27170825
复制相似问题