接受可变参数并返回可变参数的lambda函数Lua

如果我想写一个接受可变数目参数并返回一个接受可变数目参数的函数,我将遇到模糊的...,例如

    function bind(func, ...) return function(...)  func(..., ...) end end
点赞
用户2858170
用户2858170

首先,你漏掉了一个可绑定函数关闭的结束符。

如果有歧义,只需通过使用不同的名称解决它们。

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

要了解发生了什么,请阅读此部分:[https://www.lua.org/manual/5.3/manual.html#3.4.11](https://www.lua.org/manual/5.3/manual.html#3.4.11)

(当然还有Lua的其他手册)

当调用函数时,参数列表的列表被调整为参数列表的长度,除非该函数是vararg函数,该函数在其参数列表末尾表示为三个点(“...”)。 vararg函数不会调整其参数列表; 相反,它收集所有额外的参数并通过vararg表达式提供给函数,该表达式也写为三个点。这个表达式的值是所有实际额外参数的列表,类似于具有多个结果的函数。如果vararg表达式在另一个表达式中或在表达式列表中间使用,则其返回列表将调整为一个元素。如果该表达式用作表达式列表的最后一个元素,则不进行调整(除非该最后一个元素用括号括起来)。

因此,即使...是两个不同的列表,像func(...,...)这样的东西也永远不会起作用。

为避免这种情况,您必须连接两个参数列表。

function bind(func, ...)
  local args1 = table.pack(...)
  return function(...)
      local args2 = table.pack(...)

      for i = 1,args 2.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)

最后,我们得到了所需的输出:

a nil c 1 nil 3

但是我相信您可以想到更好的方法来实现您的目标,而无需连接各种varargs。

2017-04-20 07:48:52
用户2328287
用户2328287

你可以尝试使用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)
2017-04-20 09:42:57
用户8261525
用户8261525

这种方法类似于 Rici Lake 的 Partial,使用了记忆化的辅助函数而不是表格打包/解包。它有大约 2 倍的性能优势,并且具有较低的内存使用率。

local fmt,cat,pack = string.formattable.concattable.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
2017-08-03 16:08:14