Lua - Как передать функцию объекта в качестве параметра другой функции

local a = {}
function a:test1(value)
    print(value)
end
local b = {}
function b:test2(v1, v2)
    v2(100);
end
b:test2(_, a.test1)

Не работает. Значение равно нулю. Я мог найти решение, делающее инкапсуляцию в анонимной функции

b:test2(variable, function(value) a:test1(value) end)

Но я нахожу это довольно плохо mkay

Каков правильный синтаксис?

Ответ 1

anotherObject:aFunction(variable, object.doStuff) - правильный синтаксис.

Использование двоеточия : с функцией - это просто синтаксический сахар для вызова или объявления с неявным параметром self в качестве первого аргумента. Если вы хотите следовать шаблону, который вы показали в своем примере более чистым способом, вы можете использовать вспомогательную функцию.

local function bind(t, k)
    return function(...) return t[k](t, ...) end
end

Затем вы применяете его так.

anotherObject:aFunction(variable, bind(object, 'doStuff'))

Изменить: Я считаю, что решение вашей проблемы потребует привязки на каком-то уровне, не прибегая к модификации интерпретатора Lua или с помощью шага перевода кода. Это в основном потому, что функции в Lua не содержат никакой информации об их происхождении. I.e., таблицы не имеют собственных функций, которые они хранят.

Например, следующее является совершенно законным кодом Lua.

function Circle:area() -- function Circle.area(self)
    -- ...
end

-- Evaluate the function in the "area" slot with Square as the self parameter.
Circle.area(Square)

Конечно, вы можете попробовать сдвиг парадигмы, но для этого может быть слишком поздно, если вы создаете целое приложение, основанное на идее привязки функций к таблице, из которой они были проиндексированы, как вы сказали, Поэтому я предлагаю следующее сумасшедшее решение.

local mt = {}

function mt:__index(k)
    local v = self._slots[k]

    if v == nil then
        -- Ascend the inheritance tree.

        -- This has to be done with rawget all the way up,
        -- otherwise inherited functions would be repeatedly bound.
        local p = self

        repeat
            p = rawget(p, '_parent')
            if not p then break end
            v = p._slots[k]
        until v
    end

    if type(v) == 'function' then
        -- Return a self-bound version of the function.
        return function(...) return v(self, ...) end
    end

    return v
end

function mt:__newindex(k, v)
    self._slots[k] = v
end

--- Demo & Tests ---

local function Object(parent)
    local o = setmetatable({_slots = {}}, mt)
    if parent then rawset(o, '_parent', parent) end
    return o
end

local o1 = Object()
local o2 = Object(o1)

assert(o1.abc == nil, 'o1.abc should be nil')
o1.abc = 3
assert(o1.abc == 3, 'o1.abc should be 3')
assert(o2.abc == 3, 'o2.abc should be 3, inherited from o1')
o2.abc = 7
assert(o2.abc == 7, 'o2.abc should be 7, overriding o1')
assert(o1.abc == 3, 'o1.abc should be 3, unaffected by o2 setter')

function o1:test(bar)
    return self.abc + bar
end

assert(type(o1.test) == 'function', 'o1.test should be a function')
assert(type(o2.test) == 'function', 'o2.test should be a function, inherited from o1')

assert(o1.test(5) == 8, 'o1.test(5) should return 3 + 5 = 8')
assert(o2.test(11) == 18, 'o2.test(11) should return 7 + 11 = 18')

function o2:test2(fn)
    return self.abc + fn(7)
end

assert(o2.test2(o1.test) == 17, 'o2.test2(o1.test) should return 7 + (3 + 7) = 17')

o2.test3 = o1._slots.test -- proper function copying
assert(o2.test3(11) == 18, 'o2.test3(5) should return 7 + 11 = 18')

o2.abc = nil
assert(o2.abc == 3, 'o2.abc should be 3 again, inherited from o1 after clearing')

o2.abc = false
assert(o2.abc == false, 'o2.abc should be false, __index needs to differentiate between nil and false')

Этот метатет предоставит вам то, что вы хотите, с унаследованными и связанными функциями для загрузки. Вам просто нужно убедиться, что все таблицы, которые вы хотите следовать этому шаблону, также следуют методу создания объекта, показанному в примере кода.

Чтобы объяснить, каждая таблица, сделанная таким образом, имеет любое новое назначение, перенаправленное в подтаблицу _slots, и любое новое извлечение проверило дерево наследования _parent. Если тип значения равен function, он возвращает новое замыкание с исходным self, которое запустило привязку проверки к найденной функции.

Очевидно, вызов функции из одного из этих объектов с синтаксисом : двоеточия будет глупой идеей, так как он будет оценивать до o.fn(o, o), и это, вероятно, не то, что вы хотите. Еще одно предостережение заключается в том, что копирование функций на эти объекты, из этих объектов, не будет работать должным образом. o1.newfn = o2.fn поместит связанную функцию o2 в o1, которая, в свою очередь, будет привязана к o1. Конечным результатом будет что-то вроде o2.fn(o2, o1). Вам нужно будет скопировать функции из таблицы _slots.


В заключение: Несмотря на то, что это работает, я бы не рекомендовал его в долгосрочной перспективе, так как это может смутить любого, кто использует Lua для работы с таблицами, индексацией и функциями, и будут накладные расходы. Возможно, вы сможете покончить с этим через memoizing закрытия, но я оставлю это решение до вас. Удачи!

Ответ 2

ваш код будет работать. причина, по которой Райан сказал. Я сомневаюсь, что в функции anotherObject: aFunction() вы использовали неправильный способ вызова object.stuff.The правильный путь как это:

local a = {}
function a:test1()
    print(1)
end
local b = {}
function b:test2(v1, v2)
    v2();
end
b:test2(_, a.test1)