【问题标题】:Why Are My Tables Returning NIL and Not Populating?为什么我的表返回 NIL 并且没有填充?
【发布时间】:2020-08-03 17:39:46
【问题描述】:

我是 Lua 新手,并试图了解 Lua 中的 OOP 概念。为此,我尝试创建一个对象并创建方法和“私有变量”。 我的问题是,当我尝试使用“setters”或“getters”时,这表明我的表正在返回 NIL,这意味着我遇到了范围问题或其他问题否则我想不通。

关键是我正在使用来自在线 Lua 编码教程的示例,当我运行该教程时,它可以完美运行。但是,当我运行我的时,每当我尝试从成员函数之一“获取”或返回值时,我都会得到 NIL 或没有输出

我使用了几个不同的环境:

  • ZeroBrain
  • 崇高的文字
  • 适用于 Windows 的 Lua

你知道为什么我的代码没有返回填充的表格吗?

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()

【问题讨论】:

    标签: oop lua lua-table


    【解决方案1】:

    您的代码不包含要返回的“填充表”。

    您的newPlayer 函数确实创建了一个表,并且确实返回了它。它在该表中创建了许多函数。但这就是newPlayer 所做的一切:创建一个表并将一些函数放入其中。

    这些函数访问的数据不是表的一部分。 nhar(顺便说一句,请使用更好的变量名)都是局部变量。您的内部函数将访问包含这些变量的特定堆栈,但变量本身不会神奇地与表关联。

    您的主要问题几乎可以肯定与二传手有关。它来自以下的组合:

    function player:setPlayerName(arg)
    

    用这个:

    player1.getPlayerName()
    

    当您在表名和函数名之间使用: 字符创建函数时,您正在为函数使用语法糖,该函数隐式地将名为self 的值作为其第一个参数。顾名思义,这应该表示调用此函数的对象。所以你的函数创建代码相当于:

    function player.setPlayerName(self, arg)
    

    由于您使用: 创建所有函数,因此所有函数都至少采用一个参数。

    : 语法也可以在调用这样的函数时使用。如果您执行了player1:getPlayerName(),这将导致您访问的表找到getPlayerName 函数用作函数调用中的第一个参数。所以该行将等同于player1.getPlayerName(player1)

    显然,这两种语法是彼此的镜像:使用: 创建的函数带有一个参数,该参数预期引用它正在调用的表,而使用: 调用的函数将被给定 为获取该函数而访问的表。

    但是...您的代码没有遵循对称性。您使用: 创建了函数,但您调用它们使用.

    现在,您的get 函数可以摆脱这种情况,因为...好吧,您的值实际上都不是表格的一部分。因此,您的 get 函数只返回它们从创建上下文中采用的本地值。

    set 函数存在问题。看,他们接受一个参数。但是因为函数是用: 声明的,所以它们确实有两个参数,第一个是隐含的self

    现在,: 语法只是语法糖;这只是一种方便的方式来做你自己可以做的事情。所以理论上用.调用一个函数是可以的,即使你是用:创建的。但如果这样做,您必须将表格作为第一个参数传递。尽管您的代码没有显示,但我强烈怀疑您没有这样做。

    如果你调用了player1.setPlayerName("foo"),那么隐式self参数会得到"foo"的值,而arg参数会是nil。您将把 nil 值分配给 n 局部变量。所以后续对player1.getPlayerName() 的调用将返回nil

    基本上,这里发生的事情是您正在结合两种不同的方式在 Lua 中创建对象。您以外部代码无法访问的方式存储私有数据(即:本地上值),但该数据现在不再是 table 本身的一部分。这意味着,尽管您尽职尽责地使用 : 语法创建这些函数以指示它们采用 self 表,但它们实际上从未使用该表。而且因为他们从不使用这张桌子,所以很难弄清楚出了什么问题。

    基本上,这里的关键是对称。如果您使用: 创建一个函数,那么您应该使用: 调用它,或者确保将对象表作为第一个参数传递给它。


    一般来说,创建私有成员的标准方法是按照惯例,而不是通过禁止它。也就是说,您同意不与具有特定名称的表成员以外的任何成员混淆。 Python 约定是假装不存在以_ 开头的名称,而 Lua 程序有时会使用它。

    Upvalues 是私有变量的一个有趣的解决方案,但它们确实会带来一些问题。如果你想发明一个成员变量,你必须在一个集中的地方做,而不是在你可能需要的地方。即使变量是可选的,您也必须在函数顶部创建一个名为local

    【讨论】:

    • 这种编码相当于“涂鸦”,所以这就是变量命名如此短的原因。它们代表“名字”、“健康”、“盔甲”和“等级”。我正在创建玩家创建者或其他东西的伪模拟。
    • 我猜我只需要将“玩家”分配给函数中的这些变量,然后我就能正确返回它们?
    • 您能否提供一个“按约定创建私有成员,而不是禁止它”的示例? Lua 与 C++ 不同,在 C++ 中,我们至少可以为变量提供“私有”类型值。在 Lua 中,这似乎要困难得多。
    • @C0d3M0nk3y03:“我猜我只需要将“玩家”分配给函数中的这些变量,然后我就能正确返回它们?” ... 你在说什么?您的问题与“返回”的内容无关;我运行了您发布的代码,它“返回”了一个值。正如我所解释的,您的问题在于您如何调用函数,特别是set 函数。
    • @C0d3M0nk3y03: "您能否提供一个“按约定创建私有成员,而不是禁止它”的示例?" 您以某种方式命名私有变量;就这么简单。就像我说的,Python 风格是在私有变量名称前加上 _ 字符。旧式反向波兰表示法更喜欢在它们前面加上 m_。你可以在它们前面加上my_private_variable_ 或者你喜欢的任何东西。这是一个命名约定。
    【解决方案2】:

    Nicol 答案的 TLDR,请参阅 my answer 另一个问题:

      function player:setPlayerArmor(arg) 
        a = arg 
      end
    

    : 语法是语法糖。它在声明和使用时会创建一个隐式的“自我”参数。如果您以一种方式声明它并以另一种方式使用它,那么参数将不会是您所期望的。假设你的玩家有 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
    

    【讨论】:

      猜你喜欢
      • 2019-04-26
      • 2017-01-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多