如何为Lua实现bind()

  • 本文关键字: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() -- print 1

我的问题是:如何支持任意数量的约束性论点?

使用lua-vararg可以编写:

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的多个返回值将被调整为1个返回值,这是由于它在表达式中的使用方式。

你可以这样做,这将在有限的方式下工作:

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调用还是函子的参数都不允许是nil。如果是这样,这些论点以及之后的任何论点都将被剔除。

使用C API可以很容易地正确执行此操作。但由于Lua语言的限制,要实现正确的nil处理是非常困难的。

定义一个unpackN函数,该函数用于解包参数表:

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 bind(func, ...)
    local A = {...}
    return function (...)
        local B = {...}
        return func(unpackN {A, B} )
    end
end

尝试:

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))

希望能有所帮助。

最新更新