varags函数返回vargas lambdas lua

时间:2017-04-20 06:45:26

标签: lambda lua scope variadic-functions

如果我想编写一个接受varargs并返回一个接受vargas的函数的函数,我会遇到模糊的...

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

3 个答案:

答案 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