varargs 函数 return vargas lambdas lua

varags functions that return vargas lambdas lua

如果我想写一个接受可变参数的函数,returns一个接受可变参数的函数,我 运行 变成不明确的 ... 例如

    function bind(func, ...) return function(...)  func(..., ...) end end

首先,您缺少关闭绑定功能的结束。

如果您有歧义,只需使用不同的名称来解决。

function bind(func, ...)
  return function(...)  func(..., ...) end
end

如果我们这样测试您的代码:bind(print, "a", "b", "c")(1,2,3)

你会得到输出:

1 1 2 3

如果函数参数列表中有 ... 或任何其他名称,则该变量将是该函数范围内的局部变量。它将优先于上级范围内具有相同名称的任何其他变量。所以你的匿名函数中的 ... 与函数 bind 的 ... 无关。

要解决此问题,您可以简单地执行类似

的操作
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 手册的其余部分)

When a function is called, the list of arguments is adjusted to the length of the list of parameters, unless the function is a vararg function, which is indicated by three dots ('...') at the end of its parameter list. A vararg function does not adjust its argument list; instead, it collects all extra arguments and supplies them to the function through a vararg expression, which is also written as three dots. The value of this expression is a list of all actual extra arguments, similar to a function with multiple results. If a vararg expression is used inside another expression or in the middle of a list of expressions, then its return list is adjusted to one element. If the expression is used as the last element of a list of expressions, then no adjustment is made (unless that last expression is enclosed in parentheses).

所以像 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

绑定(打印,"a",零,"c")(1,零,3)

这最终给了我们想要的输出:

a nil c 1 nil 3

但我相信您可以想出一种更好的方法来实现您的目标,而无需连接各种可变参数。

您可以尝试 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)

这种方法类似于 Rici Lake's Partial,它使用记忆辅助函数而不是 table packing/unpacking。它的性能优势大约快 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