Sewbacca
Sewbacca

Reputation: 666

Is there an easy way to unpack two arrays/varargs in Lua?

Lets take a look at this pseudo code example:

-- Wraps a function with given parameters inside a callback
-- Usefull for sending class methods as callbacks.
-- E.g. callback(object.method, object)
local function callback(func, ...)
    return function(...)
         func(..., ...)
    end
end

How would I go about this? I know there is unpack, but it would get swallowed by the second vararg:

local function callback(func, ...)
    local args = { ... }
    return function(...)
         func(table.unpack(args), ...)
    end
end

callback(print, "First", "Second")("Third") --> prints First Third

The only option I found so far, is to concat those together and then unpack it:

local function callback(func, ...)
    local args1 = { ... }
    return function(...)
         local args2 = { ... }
         local args = { }
         for _, value in ipairs(args1) do
               table.insert(args, value)
         end

         for _, value in ipairs(args2) do
               table.insert(args, value)
         end

         func(table.unpack(args))
    end
end

Is this the only solution, or could I do better? What I would like to have is either a function, that concats two arrays together (which should be faster than those two for loops) and then use table.unpack on it, or make those varargs concat.

Upvotes: 4

Views: 1343

Answers (2)

Piglet
Piglet

Reputation: 28960

From the Lua 5.4 Reference Manual: 3.4 Expressions

If an expression is used as the last (or the only) element of a list of expressions, then no adjustment is made (unless the expression is enclosed in parentheses). In all other contexts, Lua adjusts the result list to one element, either discarding all values except the first one or adding a single nil if there are no values.

So the only way is to manually combine both lists to a single one befor you use them.

There are several ways to do this.

for i,v in ipairs(list2) do
  table.insert(list1, v)
end

for i,v in ipairs(list2) do
  list1[#list1+i] = v
end

table.move(list2, 1, #list2, #list1 + 1, list1)

I'm not sure what kind of problem you're actually trying to solve here. If you want to access your object from its methods use self.

-- Wraps a function with given parameters inside a callback -- Usefull for sending class methods as callbacks. -- E.g. callback(object.method, object)

Usually you would do something like this:

local callback = function(params) object:method(params) end
callback(params)

Instead of

callback(print, "First", "Second")("Third") 

You could do this

local callback = function (...) print("First", "Second", ...) end
callback("Third")

Edit ( opinonated )

My main goal is to use member functions as callbacks. However, I'd like to keep it general. The advantage of a callback function is to omit the function and end keyword. It's shorter and looks closer to what it would look like, if there would be no need for a self argument. So this: object.event = function(...) return self:method(...) end would become to this: object.event = callback(self.method, self) what would be even nicer, is this (sadly not possible in lua) object.event = self:method

So instead of

RegisterCallback(function() obj:method() end)

You say it is easier to do this, so you don't have to write function end?

local function callback(func, ...)
    local args1 = { ... }
    return function(...)
         local args2 = { ... }
         local args = { }
         for _, value in ipairs(args1) do
               table.insert(args, value)
         end

         for _, value in ipairs(args2) do
               table.insert(args, value)
         end

         func(table.unpack(args))
    end
end

RegisterCallback(callback(obj.method, obj)())

Your approach is not going to work if any of the arguments is nil to begin with. And there is not a single advantage. You're just typing other words while increasing the chance of running into errors.

Upvotes: 2

Renshaw
Renshaw

Reputation: 1155

You can control the param order to optimize your function.

local function callback(func, ...)
    local args = {...}
    return function(...)
        func(..., table.unpack(args))
    end
end

callback(print, "second", "third")("first") -- prints first second third

Upvotes: -1

Related Questions