Почему мои таблицы возвращают NIL и не заполнены? - PullRequest
1 голос
/ 21 апреля 2020

Я новичок в Lua и пытаюсь понять концепцию OOP в Lua. Для этого я попытался создать объект и создать методы и «частные переменные». Моя проблема в том, что когда я пытаюсь использовать «сеттеры» или «геттеры», это означает, что мои таблицы возвращают NIL , что означает, что у меня либо проблема с областью видимости, либо я не могу понять.

Кикер, я использую пример из онлайнового Lua учебника по кодированию, и когда я запускаю учебник, он работает безупречно. Однако, когда я запускаю мой, я получаю NIL или ничего не выводится всякий раз, когда я пытаюсь "получить" или вернуть значение из одной из функций-членов.

Я использую пару различные среды:

  • ZeroBrain
  • Sublime Text
  • Lua для Windows

Знаете ли вы, почему мой код не возвращает заполненные таблицы?

newPlayer = function(n, h, a, r)

  player = {}

  n = n or ""
  h = h or 100
  a = a or 100
  r = r or 0

  function player:getPlayerName() 
    return n 
  end

  function player:getPlayerHealth() 
    return h 
  end

  function player:getPlayerArmor() 
    return a 
  end

  function player:getPlayerRank()
    return r 
  end


  function player:setPlayerName(arg) 
    n = arg 
  end

  function player:setPlayerHealth(arg) 
    h = arg 
  end

  function player:setPlayerArmor(arg) 
    a = arg 
  end

  function player:setPlayerRank(arg) 
    r = arg 
  end

  function player:connect(arg)
    print(string.format(" %s joined" , arg)) 
    end

  return player

end

player1 = newPlayer("John", 100, 100, 1000)
player1.getPlayerName()

Ответы [ 2 ]

4 голосов
/ 21 апреля 2020

Ваш код не содержит "заполненных таблиц" для возврата.

Ваша функция newPlayer создает таблицу и возвращает ее. Он создает ряд функций в этой таблице. Но это все, что делает newPlayer: создает таблицу и помещает в нее некоторые функции.

Данные , к которым получают доступ эти функции, не являются частью таблицы. n, h, a и r (кстати, используйте более подходящие имена переменных) - все это локальные переменные. Ваши внутренние функции будут обращаться к указанному c стеку, содержащему эти переменные, но сами переменные не будут магически связаны с таблицей.

Ваша принципиальная проблема почти наверняка связана с установщиками. И это происходит из комбинации этого:

function player:setPlayerName(arg)

с этим:

player1.getPlayerName()

Когда вы создаете функцию, используя символ : между именем таблицы и именем функции, Вы используете syntacti c sugar для функции, которая неявно принимает в качестве первого аргумента значение, называемое self. Как следует из названия, это должно представлять объект, к которому эта функция вызывается. Таким образом, код создания вашей функции эквивалентен:

function player.setPlayerName(self, arg)

Поскольку вы создаете все свои функции с помощью :, все ваши функции принимают хотя бы один параметр.

: синтаксис также может использоваться при вызове таких функций. Если вы сделали player1:getPlayerName(), это приведет к тому, что в таблице, к которой вы обратились, будет найдена функция getPlayerName, которая будет использоваться в качестве первого аргумента в вызове функции. Таким образом, эта строка будет эквивалентна player1.getPlayerName(player1).

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

Но ... ваш код не придерживался симметрии. Вы создали функции с помощью :, но вы вызываете их с помощью .

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

Функции set представляют проблему. Видите, они принимают параметр. Но поскольку функция была объявлена ​​с :, они действительно принимают два параметра , первый из которых неявный self.

Теперь синтаксис : - это просто syntacti c сахар; это просто удобный способ сделать то, что вы могли бы сделать сами. Так что теоретически нормально вызывать функцию с ., даже если вы создали ее с :. Но если вы сделаете это, вы должны передать таблицу в качестве первого параметра. Хотя ваш код этого не показывает, я сильно подозреваю, что вы этого не сделали.

Если вы вызвали player1.setPlayerName("foo"), то получится, что неявный параметр self получит значение "foo" и параметр arg будет nil. И вы назначите это значение nil локальной переменной n. Поэтому последующие вызовы player1.getPlayerName() вернут nil.

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

По сути, здесь ключ к симметричности. Если вы создаете функцию с :, то вам следует либо вызвать ее с :, либо обязательно передать ей таблицу объектов в качестве первого параметра.


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

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

0 голосов
/ 23 апреля 2020

TLDR ответа Никола, см. мой ответ на другой вопрос:

  function player:setPlayerArmor(arg) 
    a = arg 
  end

Синтаксис : - это синтаксис c сахар. Он создает неявный аргумент self при объявлении и использовании. Если вы объявите это одним способом, а используете другим, аргументы не будут такими, как вы ожидаете. Скажем, у вашего игрока 100 единиц здоровья. Посмотрите на этот результат:

player1.setPlayerHealth(55, 66)
print(player1.getPlayerHealth())
-- will display '66', not '55' because `:` declares implicit 'self' argument

Это отображает 66, потому что функция setPlayerHealth имеет неявный параметр self, потому что она была объявлена ​​с :. Если вы вместо этого позвонили с ::

player1:setPlayerHealth(55, 66)
print(player1:getPlayerHealth())
-- will display '55' because `:` passes player1 as self
function player:setHealth1(arg)
  -- implicit 'self' argument refers to player1 when called on player1
end

-- is the same as
function player.setHealth2(self, arg)
  -- with `.` notation, you need to add the 'self' argument explicitly
end

player1.setHealth1(31) -- self argument will be 31 and arg will be nil
player1.setHealth2(32) -- self argument will be 32 and arg will be nil
player1:setHealth1(33) -- self argument will be player1 and arg will be 33
player1:setHealth2(34) -- self argument will be player1 and arg will be 34
...