如果我想编写一个接受varargs并返回一个接受vargas的函数的函数,我会遇到模糊的...
function bind(func, ...) return function(...) func(..., ...) end end
答案 0 :(得分:5)
首先,你错过了关闭绑定功能的结束。
如果您有歧义,只需使用不同的名称解决它们。
function bind(func, ...)
return function(...) func(..., ...) end
end
如果我们测试您的代码:bind(print, "a", "b", "c")(1,2,3)
您将获得输出:
1 1 2 3
如果在函数参数列表中有...或任何其他名称,则该变量将是该函数范围内的本地变量。它优先于优越范围内具有相同名称的任何其他变量。所以...在你的匿名函数中与函数绑定无关。
要解决此问题,您可以执行类似
的操作function bind(func, ...)
local a = table.pack(...)
return function(...) func(table.unpack(a, 1, a.n), ...) end
end
现在调用bind(print," a"," b"," c")(1,2,3)将输出:
a 1 2 3
要了解b和c发生了什么,请阅读本节:https://www.lua.org/manual/5.3/manual.html#3.4.11
(当然还有Lua手册的其余部分)
调用函数时,参数列表会调整为 参数列表的长度,除非该函数是vararg 功能,在其末尾用三个点(' ...')表示 参数列表。 vararg函数不调整其参数列表; 相反,它收集所有额外的参数并将它们提供给 函数通过vararg表达式,也写成三个 点。此表达式的值是所有实际额外值的列表 参数,类似于具有多个结果的函数。 如果是vararg 表达式在另一个表达式中或在a的中间使用 表达式列表,然后将其返回列表调整为一个元素。 如果表达式用作列表的最后一个元素 表达式,然后没有进行调整(除非最后一个表达式 括在括号中。
所以像func(...,...)这样的东西永远不会起作用,即使......是两个不同的列表。
为了避免这种情况,你必须连接两个参数列表。
function bind(func, ...)
local args1 = table.pack(...)
return function(...)
local args2 = table.pack(...)
for i = 1, args2.n do
args1[args1.n+i] = args2[i]
end
args1.n = args1.n + args2.n
func(table.unpack(args1, 1, args1.n))
end
end
bind(打印," a",nil," c")(1,零,3)
最终为我们提供了所需的输出:
a nil c 1 nil 3
但我相信你可以想出一个更好的方法来实现你的目标,而不会连接各种各样的varargs。
答案 1 :(得分:2)
您可以尝试使用vararg库。这也在参数中处理nil
。
va = require "vararg"
function bind(func, ...)
local args = va(...)
return function(...)
func(va.concat(args, va(...)))
end
end
bind(print, 1, nil, 2)(3, nil, 5)
答案 2 :(得分:0)
这种方法类似Rici Lake's Partial,它使用memoized辅助函数而不是表打包/解包。它的性能优势大约快2倍,并且内存使用率更低。
local fmt, cat, pack = string.format, table.concat, table.pack
local function args(n,Prefix)
local as,prefix = {}, Prefix or '_'
local step,from,to = n<0 and -1 or 1, n<0 and -n or 1, n<0 and 1 or n
for i=from,to,step do as[1+#as]=prefix..i end
return function(sep) return cat(as,sep or ',')end
end
local function paramsCat(...)
local r,p = {}, pack(...)
for i=1,p.n do if p[i]:len()>0 then r[1+#r]=p[i] end end
return cat(r,',')
end
local bind = setmetatable({}, {
__call = function(self,f,...)
local narg = select("#",...)
if not self[narg] then
local a = args(narg)()
local b = '...'
local src = fmt([[
return function(%s) -- _1,_2
return function(fn)
return function(...)
return fn(%s) -- _1,_2,...
end
end
end]],a, paramsCat(a,b))
local fn = load(src,'_')()
self[narg] = fn
end
return self[narg](...)(f)
end
})
稍作修改,我们可以将绑定扩展为从第n个参数开始,
local bindn = setmetatable({}, {
__call = function(self, n, f, ...)
local narg = select("#",...)
if type(n)~='number' then -- shifted, n is the function now
if f~=nil or narg>0 then
return self(0, n, f, ...)
else
return self(0, n)
end
end
self[n] = self[n] or {}
if not self[n][narg] then
local a = args(n)()
local b = args(narg,'b')()
local c = '...'
local src = fmt([[
return function(%s) -- b1,b2
return function(fn)
return function(%s) -- _1,_2,_3,...
return fn(%s) -- _1,_2,_3,b1,b2,...
end
end
end]],b, paramsCat(a,c), paramsCat(a,b,c))
local fn = load(src,'_')()
self[n][narg] = fn
end
return self[n][narg](...)(f)
end
})
local dp = bindn(2,print,'debug-','print:')
dp(1,2,3,4,5) --> 1 2 debug- print: 3 4 5
dp = bindn(print,'debug-','print:')
dp(1,2,3,4,5) --> debug- print: 1 2 3 4 5
string.Bytes = bindn(1,string.byte,1,-1)
print(("Test"):Bytes()) --> 84 101 115 116