【问题标题】:Lua Object-Oriented Programming - I don't understand how to new up an instance of a "class"?Lua 面向对象编程 - 我不明白如何新建一个“类”的实例?
【发布时间】:2014-01-13 00:52:53
【问题描述】:

我正在构建一个简单的游戏,并且一直在关注 PIL 书。这是我的游戏基于的场景: http://www.lua.org/pil/16.1.html

我很苦恼,因为书中只有一个模块,而我试图让 8 个模块一起工作。

这是我游戏的一小部分:我现在有一个游戏模块、一个棋盘模块和一个输入/输出模块。

我以为我了解何时使用冒号以及何时使用句号,但我不明白为什么在 PIL 示例中,作者将“o”传递给“新”方法以及为什么有一个冒号用那个方法?

我的游戏模块应该是我的最高级别模块。在其中,我想我会更新电路板和输入/输出模块并让它们一起工作。但是对于板卡和输入/输出模块来说,这些“新”(即初始化)方法是什么样的呢?

这是我一直在使用的一些代码(简化):

Game = {}

function Game:new(o)
  o = {} or o
  setmetatable(o, self)
  self.__index = self
  -- need board and input_output instances below
  o.board = Board:new(o)
  o.input_output = Input_Output:new(o)
  return o
end

return Game
----------------------------------------------------
Board = {}

function Board:new(o)
  o = o or {}
  -- need layout attribute here
  o.layout = { create_layout() }
  setmetatable(o, self)
  self.__index = self
  return o
end

return Board
---------------------------------------------------
Input_Output = {}

function Input_Output:new(o)
  o = o or {}
  -- need read and write attributes here
  o.read = stdin
  o.write = stdout
  setmetatable(o, self)
  self.__index = self
  return o
end

return Input_Output

在像 Ruby 这样的 OOP 语言中,Game 将保存我的 Board 和 Input_Output 类的实例。然后,如果我深入到 game.board,我可以看到板上的公共属性和公共方法。

但是,当我在游戏中更新这两个“职业”时,发生了一些奇怪的事情。我的 self 和 o 变量不是我所期望的(我正在使用 lua_inspect)。我似乎可能会用模块的每个新实例覆盖 o 变量中的数据?

我很迷茫,我认为这是因为“新”方法。我就是不明白。

谁能解释一下?我的问题主要是 - 示例中的“o”是什么,为什么“新”方法上有一个冒号?

【问题讨论】:

  • 附带说明一下,有很多面向对象的实现(例如yonaba.github.io/30log)。您可以使用它们而不是自己管理元表。

标签: oop lua


【解决方案1】:

我试图解释这一点。这也将帮助我更深入地理解这一点。

那么,看看http://www.lua.org/pil/16.1.html中的例子吧。

Account = {}
function Account:new (o)
  o = o or {}   -- create object if user does not provide one
  setmetatable(o, self)
  self.__index = self
  return o
end

了解 Lua 中的函数

function foo (x) return 2*x end

只是我们所说的语法糖的一个实例;换句话说,这只是一种很好的写作方式

foo = function (x) return 2*x end

您知道: 只是. 的句法工具。

function Account:new (o)

function Account.new ( Account, o )

所以最重要的是,我们知道

function Account:new (o)

Account.new = function( Account, o )

我们如何在 Lua 表中找到一些东西

a = Account.new

查找过程如下:

Find if there is a 'new' key in `Account`? --Yes-- Return `Account.new`
               |
               No
               |
Check if `Account` has a metatable? --No-- Return `nil`
                |
                Yes
                |
Check if there is a 'new' key in the `__index` field of `Account`'s metatable --No-- Return `nil`
                |
                Yes
                |
    Assume `metaAccount` is the metatable of Account
    Return `metaAccount.__index.new`

Account:new 做了什么

o = o or {} -- just create a new table if the input o is nil

setmetatable(o, self) -- self is Account because of `:`, and set o's metatable to Account

self.__index = self -- the same as Account.__index = Account, this is set the `__index` field of Account, which is o's metatable

return o --return o, a table which has a metatable `Account`, and `Account` has a `__index` field is also `Account`

工作原理

我们定义另一个函数

function Account:print() --the same as Account.print = function( self )
    print("Class Account.")
end

a = Account:new() --a is a table which has a metatable `Account`, and `Account` has a `__index` field is also `Account`
a:print() --find a.print in a's metatable `Account` as a function, and run it
-->Class Account.

你现在应该清除...

【讨论】:

  • 非常感谢您的帮助!您的回答也帮助我解决了其他一些令我感到困惑的事情。
【解决方案2】:

实际上 Lua 中没有“官方”类,所以你可以按照你想要的任何方式来实现它们。在这个特定的实现中,o 是新实例,可能带有一些预定义的参数。它继承了 parent 的所有成员,无论是值还是方法 - 函数 - 在 Lua 中都是一样的。如果您在此调用中根本没有提供 o,则会为您创建一个空表。

函数定义中的: 只是function Game.new(self, o) 的语法糖——也就是说,它添加了一个名为self 的第一个参数。

你应该像local my_game = Game:new()一样调用这个函数。之后,my_game 将是一个包含my_game.boardmy_game.input_output 成员的表。

您当前的问题并不清楚您看到了什么以及它与您的预期有何不同。如果您提供更多详细信息,我也可以添加更多详细信息来回答。

【讨论】:

  • 非常感谢!我现在看到“o”不是必需的。我认为这是并且对此感到困惑,所以我选择了你的答案。
  • o 如果要预定义某个类,可以用作初始化。假设你的Game 类有level,默认是0。您可以使用local my_game = Game:new({ level = 3}) 对其进行初始化,之后my_game.level 将从一开始就是3
【解决方案3】:

所以要关注“示例中的'o'是什么以及为什么“新”方法上有一个冒号?”:

所以 Game 是类:它是一个带有函数的表。这就是 Lua 中类的基本概念。那么什么是游戏实例呢?它是一个表,其元表是其类的表,即 Game。

但在 Lua 中,表中的函数无法知道它在哪个表中,除非它被赋予此信息作为调用参数。 Lua 通过自动引用包含表作为函数的第一个参数你用冒号而不是点来调用它,使这很容易。第一个参数的名称为“self”。所以在“new”函数中,用冒号调用它提供了new被定义为self参数的表,即Game类。

所以你可以看到'o'是你正在创建的实例,你用冒号调用new,因为否则你必须给它一个类表,然后它会在你的语句中出现两次,即冒号提供了更好的语法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-04-10
    • 2022-01-05
    • 2019-09-29
    • 1970-01-01
    • 1970-01-01
    • 2014-09-04
    • 2018-10-30
    • 2018-01-12
    相关资源
    最近更新 更多