Lua - Как мне динамически вызывать модуль?

Вот немного упрощенного кода Lua, с которым я работаю. Мне нужно знать, как динамически вызывать другой модуль («зебра»):

avar = require "avar"
bvar = require "bvar"

function create(zebra)
  print(zebra.new())
end

print(create(avar))

А вот и два модуля:

local Avar = {}

function Avar.new()
  return "avar"
end

return Avar

local Bvar = {}

function Bvar.new()
  return "new"
end

function Bvar.old()
  return "old"
end

return Bvar

Если я попытаюсь передать строку «avar» моей функции «создать», это не сработает. Если я передам слово «авар» без кавычек, оно сработает, однако я не понимаю, что такое авар без кавычек? Кажется, это пустая таблица? Не уверен, как передать пустую таблицу в качестве аргумента в моей основной программе.

Но, может быть, я совершенно на неправильном пути. Как динамически вызывать модули?


person Kelly    schedule 13.01.2014    source источник


Ответы (3)


Вы можете потребовать в любое время:

function create(zebraModuleName)
  zebraType = require(zebraModuleName)
  print(zebraType .new())
end

print(create("avar"))
print(create("bvar"))
person Oliver    schedule 13.01.2014
comment
Спасибо! Это именно то, что мне было нужно. - person Kelly; 13.01.2014

avar без кавычек — это созданная вами глобальная переменная. Он инициализируется значением, возвращаемым функцией require1, то есть значением, возвращаемым модулем, который вы вызываете. В данном случае это таблица с полем new, которое оказывается функцией.

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

Кроме этого, есть и другие вещи, которые вы здесь путаете:

  • Таблица, которую вы сохраняете на avar, не пуста! Вы можете распечатать его содержимое, выполнив for k,v in pairs(avar) do print(k,v) end, чтобы увидеть это.

  • Переменные avar, bvar и create по умолчанию являются глобальными и будут видны другим модулям. В большинстве случаев вы бы предпочли сделать их локальными.

    local avar = -- ...
    local bvar = -- ...
    
    local function create (zebra)
      -- ...
    end
    
  • Функция создания явно ожидает таблицу, поскольку она индексирует таблицу по своему аргументу (получая ключ new и вызывая его). Строка не имеет «нового» ключа, поэтому она не будет работать.

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

  • create всегда возвращает nil, поэтому нет смысла делать print(create(avar)). Вероятно, вы хотите изменить create, чтобы вернуть его объект, а не печатать его.

person hugomg    schedule 13.01.2014
comment
Спасибо за все это. Я взял этот фрагмент кода из всей программы, где большинство функций не находятся в глобальном пространстве имен. Но все же я думаю, что все мои модули НЕ являются локальными (согласно вашему пункту 2). Вопросы: Как сделать модули локальными? Должен ли я тогда переходить на авар без кавычек? Согласно пункту 4, должен ли я требовать динамически? Я чередую только 1 из 3 модулей, поэтому могу потребовать их все, но просто хочу делать то, что правильно. Спасибо за помощь. - person Kelly; 13.01.2014
comment
@Kelly: Попробуйте прочитать код Lua вместо того, чтобы слепо гадать, что делать. Не существует такого понятия, как локальный модуль — локальным или глобальным может быть значение, которому вы назначаете таблицу модулей. (В данном случае local avar = против avar =). Что касается выбора используемого модуля, вы можете либо загрузить все модули и выбрать одно из значений с помощью if-else-if или хеш-таблицы, либо вы можете передать переменную с именем модуля для запроса вместо передачи литеральной строки. Требовать всего проще, поэтому я бы сделал это, если только это не окажется проблемой производительности. - person hugomg; 13.01.2014

Вы можете использовать стандартный require из языка lua или создать свой собственный загрузчик, используя метатаблицы/метометоды.

<сильный>1. создать глобальную функцию:

function dynrequire (module)                                                                                           
  return setmetatable ({},{
    __index = function (table,key)
      return require(module..'.'..key)
    end
  })
end

<сильный>2. Создайте дерево проекта, видимое для package.path

./MySwiss/
  \___ init.lua
  \___ cut.lua
  \___ glue.lua
  \___ dosomething.lua

<сильный>3. Сделайте свой модуль динамическим, вам нужно только поместить эту строку в свой MySwiss/init.lua (как если бы вы создавали пространство имен для класса PHP):

return dynrequire('MySwiss')

<сильный>4. Требовать ваш модуль и динамически использовать вложенные свойства В вашем скрипте вам нужно только потребовать MySwiss и файл папки (или подпапки с dynrequire('MySwiss.SubFolderName').

var X = require('MySwiss')
X.glue()

Обратите внимание, что у MySwiss нет клеевого ключа. Но когда вы пытаетесь получить доступ к ключу клея, метаметод __index пытается потребовать подмодуль. Используя эту технику, вы можете создать полное дерево проекта. Единственным недостатком являются внешние зависимости, которые не упакованы таким образом.

person Thadeu de Paula    schedule 01.08.2019