如何为Lua实现bind()?

如何为Lua实现bind()?,lua,Lua,我想为Lua实现bind(),它在Javascript中广泛用于创建闭包 下面的代码演示了1个参数案例: function bind(func, arg1) return function (...) return func(arg1, ...) end end local x = { data = 1 } function x.print(self) print self.data end outputX = bind(x.print, x) outputX() --

我想为Lua实现
bind()
,它在Javascript中广泛用于创建闭包

下面的代码演示了1个参数案例:

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

local x = { data = 1 }
function x.print(self)
  print self.data
end

outputX = bind(x.print, x)
outputX() -- print 1
我的问题是:如何支持任意数量的绑定参数

local va = require "vararg"

function bind(f, ...)
  local outer_args = va(...)
  local function closure (...)
    return f(va.concat(outer_args, va(...)));
  end
  return closure;
end

bind(print, 1, 2, 3)(4,5,6)
这是纯lua实现

function packn(...)
  return {n = select('#', ...), ...}
end

function unpackn(t)
  return (table.unpack or unpack)(t, 1, t.n)
end

function mergen(...)
  local res = {n=0}
  for i = 1, select('#', ...) do
    local t = select(i, ...)
    for j = 1, t.n do
      res.n = res.n + 1
      res[res.n] = t[j]
    end
  end
  return res
end

function bind(func, ...)
  local args = packn(...)
  return function (...)
    return func(unpackn(mergen(args, packn(...))))
  end
end

bind(print, 1, nil, 2, nil)(3, nil, 4, nil)

由于Lua处理
和多个返回值的方式,明显的方式不起作用:

function bind(func, ...)
  local args = {...}
  return function (...)
    return func(unpack(args), ...)
  end
end
这将失败,因为
unpack
中的多个返回值将被删除

您可以这样做,这将以有限的方式工作:

function bind(func, ...)
  local nargs = select("#", ...)
  local args = {...}
  return function (...)
    local newArgs = {...}
    local fullArgs = {}
    copy(fullArgs, args)
    copy(fullArgs, {...})
    return func(unpack(fullArgs))
  end
end
上面的
copy
函数只是一个简单的实用函数,它将数组元素从一个表复制到另一个表

这里的限制是,
bind
调用或functor的任何参数都不允许为
nil
。如果是的话,这些论据以及之后的任何论据都将被剔除


使用C API正确地执行此操作将非常容易。但是,由于Lua语言的限制,使用适当的
nil
处理是非常困难的。

定义一个
解压
函数来解压参数表:

function unpackN(argss, i)
    i = i or 1

    local iLocal = i
    for _, args in ipairs(argss) do
        local argsN = #args

        if iLocal <= argsN then
            return args[iLocal], unpackN(argss, i+1)
        end

        iLocal = iLocal-argsN
    end
end
尝试:

现在您有了一个可变绑定:

function add(...)
    local sum = 0

    for i = 1, select("#", ...) do
        sum = sum + select(i, ...)
    end

    return sum
end

local add_2_3 = bind(add, 2, 3)

print(add_2_3(5))

希望对您有所帮助。

要支持nil,您的
copy
函数应该检索两个附加参数<代码>位置和
计数
<代码>复制(fullArgs,args,1,nargs);复制(fullArgs,{…},nargs+1,选择('#',…)No您可以
unpack(fullArgs,1,nargs+select('#',…)
您的代码不能使用nil。尝试测试绑定(print,1,nil,2,nil)(3,nil,4,nil)
{1,2,nil,3}==4
{1,2,nil,3,nil}==2
。有点困惑。。好的,我学到了永远不要在数组中放入零。
function bind(func, ...)
    local rest = {...}

    return function (...)
        local args = {}

        for i = 1, #rest do
            args[i] = rest[i]
        end

        for i = 1, select("#", ...) do
            table.insert(args, select(i, ...))
        end

        return func(unpack(args))
    end
end
function add(...)
    local sum = 0

    for i = 1, select("#", ...) do
        sum = sum + select(i, ...)
    end

    return sum
end

local add_2_3 = bind(add, 2, 3)

print(add_2_3(5))