Varags functions returning vargas lambdas lua
First of all, you are missing the end to close the binding function.
If you have any ambiguities, just resolve them by using different names.
function bind(func, ...)
return function(...) func(..., ...) end
end
If we test your code like this: bind(print, "a", "b", "c")(1,2,3)
you get the result:
1 1 2 3
If you have ... or any other name in the parameter list of functions, this variable will be local within that function. It will take precedence over any other variable of the same name in excellent scope. So ... in your anonymous function has nothing to do with ... the bind function.
To fix this problem, you can simply do something like
function bind(func, ...)
local a = table.pack(...)
return function(...) func(table.unpack(a, 1, a.n), ...) end
end
Now bind (print, "a", "b", "c") (1,2,3) is called:
a 1 2 3
To find out what happened to b and c, read this section: https://www.lua.org/manual/5.3/manual.html#3.4.11
(and of course the rest of the Lua manual)
When the function is called, the argument list is set to the length of the parameter list, unless the function is a vararg function, denoted by three dots ("...") at the end of the parameter list. The vararg function does not update the argument list; instead, it collects all additional arguments and supplies them to the function via a vararg expression, which is also written as three dots. The value of this expression is a list of all the actual optional arguments, similar to a function with multiple results. If a vararg expression is used within another expression or in the middle of a list of expressions, then its returned list is set to one element... If the expression is used as the last element of the list of expressions, then no adjustment is made (if this last expression is enclosed in parentheses).
So something like func (..., ...) would never work, even if ... there were two different lists.
To avoid this, you need to match both argument lists.
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 (print, "a", nil, "c") (1, nil, 3)
Which finally gives us the desired output:
a nil c 1 nil 3
But I'm sure you can think of a better way to achieve your goal without concatenating various varargs.
source to share
This approach is similar to Rici Lake Partial , which uses memoized helper functions instead of boxing / unpacking tables. It has a performance advantage of about 2x faster and has lower memory usage.
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
})
With a little modification, we can extend the binding to start with the nth argument,
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
source to share