Передача объекта в функцию Lua всегда ссылается на себя - PullRequest
1 голос
/ 12 октября 2019

Я пытаюсь создать объект ("комнату") в Lua, у которого есть функция, чтобы проверить, пересекается ли другая предоставленная комната с ним.

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

Room = {
    Width = 0,
    Height = 0,
    X = 0,
    Left = 0,
    Right = 0,
    Top = 0,
    Bottom = 0
}

function Room:new(width, height, x, y)
    self.Width = width
    self.Height = height
    self.X = x
    self.Y = y
    self.Left = x
    self.Right = self.X + self.Width
    self.Top = y
    self.Bottom = self.Y + self.Height

    return self
end

function Room:Intersects(other)
    print("Checking for intersection...")
    print("Self X: ", self.X)
    print("Self Y: ", self.Y)
    print("Other X: ", other.X)
    print("Other Y: ", other.Y)
    return other.Left < self.Right and self.Left < other.Right and other.Top < self.Bottom and self.Top < other.Bottom
end

room1 = Room:new(5, 6, 3, 3)
room2 = Room:new(10, 16, 5, 9)

intersects = room1:Intersects(room2)
print("Intersects: ", intersects)

Вывод:

$ lua FunctionTest.lua 
Checking for intersection...
Self X:     5
Self Y:     9
Other X:    5
Other Y:    9
Intersects:     true

Я ожидаю, что self.X, self.Y будут отличаться от other.X и other.Y. Я следил за главой Lua.org по объектно-ориентированному программированию .

Ответы [ 2 ]

2 голосов
/ 14 октября 2019

Как говорит Дак, определенную вами таблицу Room вы также возвращаете как «новый» объект Room. Room:method(...) - это сокращение синтаксиса для Room.method(Room, ...)

. Переменная self, предоставляемая Lua, содержит ссылку на Room, когда вы используете сокращение, поэтому при вводе Room:new(5, 6, 3, 3) то, что читает Lua, равно Room.new(Room, 5, 6, 3, 3)с self = Room. Когда мы тогда допустим self.width = 5, то получится Room.width = 5, и это не то, что мы хотели бы сделать.

Чтобы решить эту проблему, нам нужно создавать новый объект каждый раз, когда вызывается Room:new. Мы создаем новую таблицу с именем obj и сохраняем в ней значения.

function Room:new(width, height, x, y)
    local obj = {
        Width = width,
        Height = height,
        X = x,
        Y = y,
        Left = x,
        Right = x + width,
        Top = y,
        Bottom = y + height,
    }
    return obj
end

Это работает хорошо, теперь мы можем создавать комнаты. Но когда мы пытаемся сделать room1:Intersects(other), мы получаем ошибку: method Intersects not defined. Возможно, мы определили новую комнату, но эта комната - не более чем простой стол. Вот где приходят метатаблицы. Проще говоря, метатаблицы определяют поведение таблицы. В нашем случае мы хотим, чтобы это содержало Lua для просмотра, когда он не может найти значение или метод в исходном списке. (См. глава 13 для получения дополнительной информации о метатаблицах.)

Давайте рассмотрим пример:Если бы этаж в каждой комнате был бы одинаковым, мы могли бы либо скопировать этот этаж каждому ребенку, либо указать Room.floor = "Carpet" и вернуться к этому, когда кто-то спросит нас, каков этаж в определенной комнате. Чтобы вернуться к этому, мы используем метатаблицы и метод __index. Этот метод - то место, которое Lua будет искать значения, которые он не может найти.

function Room:new(width, height, x, y)
    local obj = {
        ...
    }
    setmetatable(obj, self) -- self here is equal to Room
    self.__index = self
    return obj
end

Снова мы создаем объект со всеми значениями, которые мы хотим сохранить для каждой комнаты отдельно. Во-вторых, мы допустим метатаблицу Room для obj. Третья строка говорит нам, что таблица Room должна использоваться, когда невозможно найти какие-либо методы. Использование self.__index = self при создании объекта будет таким же, как при написании Room.__index = Room при определении самой комнаты, но помогает, когда вы просто копируете один и тот же / похожий код каждый раз :)Если мы определили, что Room.floor = "Carpet", результатом print(room1.floor) будет Carpet, как и ожидалось. Значение room1.floor равно nil, тогда Lua будет искать __index в метатаблице room1, где он обнаружит, что Room может содержать то, что он искал. И действительно, Room.floor определено, поэтому Луа видит в этом ответ.

Сводка

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


Кроме того, если вы хотите, чтобы Room:Intersects выдавало false, когда вы делаетечто-то вроде room1:intersects(room1), просто добавьте self ~= other. Это удастся, только если self и other - это одна и та же таблица, даже если все значения комнаты одинаковы, они не будут равны и пересекаются друг с другом.

2 голосов
/ 12 октября 2019

Комната - это отдельная таблица, определенная вверху, и все ссылки на Комнату действуют на эту одну и ту же таблицу вместо разных объектов.

Приведенное ниже решение создает другую таблицу с именем 'obj 'и возвращает эту уникальную таблицу каждый раз, когда вызывается NewRoom (). Этот подход очень похож на главу 16.4 руководства, на которое вы ссылались.

function NewRoom(width, height, x, y)
    local obj = {
      Width = width,
      Height = height,
      X = x,
      Y = y,
      Left = x,
      Right = x + width,
      Top = y,
      Bottom = y + height,
    }

    function obj:Intersects(other)
      print("Checking for intersection...")
      print("Self X: ", self.X)
      print("Self Y: ", self.Y)
      print("Other X: ", other.X)
      print("Other Y: ", other.Y)
      return other.Left < self.Right and self.Left < other.Right and other.Top < self.Bottom and self.Top < other.Bottom
    end

    return obj
end

room1 = NewRoom(5, 6, 3, 3)
room2 = NewRoom(10, 16, 5, 9)

intersects = room1:Intersects(room2)
print("Intersects: ", intersects)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...