Lua:什么时候可以使用冒号语法?

blu*_*e10 5 lua

虽然我理解之间.:的基本区别,但我还没有完全弄清楚Lua何时允许使用冒号语法.例如,像这样的东西确实有效:

s = "test"
-- type(s) is string.
-- so I can write a colon function for that type
function string:myFunc() 
  return #self 
end

-- and colon function calls are possible
s:myFunc()
Run Code Online (Sandbox Code Playgroud)

但是,相同的模式似乎不适用于其他类型.例如,当我有一个table而不是string:

t = {}
-- type(t) is table.
-- so I can write a colon function for that type
function table:myFunc() 
  return #self 
end

-- Surprisingly, a colon function call is not not possible!
t:myFunc() -- error: attempt to call method 'myFunc' (a nil value)
-- But the verbose dot call works
table.myFunc(t)
Run Code Online (Sandbox Code Playgroud)

转到另一种类型:

x = 1
-- type(x) is number.
-- So I was expecting that I can write a colon function 
-- for that type as well. However, in this case even this
-- fails: 
function number:myFunc() 
  return self 
end
-- error: attempt to index global 'number' (a nil value)
Run Code Online (Sandbox Code Playgroud)

我目前正试图理解这一点.结论是正确的

  • 某些类型,例如string允许冒号函数定义冒号函数调用.
  • 其他类型,例如table只允许冒号 - 函数 - 定义,但不允许冒号 - 函数调用.
  • 但其他类型也number不允许.

这些差异究竟是什么原因?是否有所有类型的列表,显示它们支持哪种类型的冒号语法?是否可能有一个解决方法number,允许写例如x:abs()

blu*_*e10 6

作为一个完全的Lua新手,我花了一段时间才理解@Yuhao的回答,所以我会尝试为其他初学者添加一些细节。如果有什么不对的地方请指正。

据我所知,像这样的调用x:someFunc()有效 if [*]:

  • x有一个元表
  • 元表有一个字段__index
  • 它指向一个包含函数的表someFunc

正如于浩所指出的,字符串会自动获得一个指向表的元表string,例如:

th> s = 'test'

th> getmetatable(s)
{
  __mod : function: 0x40c3cd30
  __index : 
    {
      upper : function: builtin#82
      rep : function: builtin#79
      split : function: 0x40ffe888
      gfind : function: builtin#87
      find : function: builtin#84
      reverse : function: builtin#80
      lower : function: builtin#81
      len : function: 0x40af0b30
      tosymbol : function: 0x40ffe8a8
      myFunc : function: 0x41d82be0 -- note: this comes from our custom function string:myFunc()
      dump : function: builtin#83
      byte : function: builtin#76
      char : function: builtin#77
      gmatch : function: builtin#87
      match : function: builtin#85
      sub : function: builtin#78
      gsub : function: builtin#88
      format : function: builtin#89
    }
}
Run Code Online (Sandbox Code Playgroud)

所以在这种情况下s:myFunc()会自动工作。为了对 a 使用冒号语法table,我们可以手动设置它的元表:

th> function enableColonForTable(t)
..>   meta = {__index = table}
..>   setmetatable(t, meta)
..> end

th> t = {}

th> enableColonForTable(t)

th> t:insert(1) -- works now!
Run Code Online (Sandbox Code Playgroud)

__index另一个观察结果是,是否指向与类型具有完全相同名称的表实际上并不重要。除了meta = {__index = table},我们还可以这样做:

th> arbitraryScope = {}

th> function arbitraryScope:test() return "something" end

th> t = {}

th> setmetatable(t, {__index = arbitraryScope})
{}

th> t:test()
something   
Run Code Online (Sandbox Code Playgroud)

这也是与 a 情况的关键区别number。虽然存在名为string和的现有表table,但不存在名为 的现有表number。这就是为什么function number:abs()之前定义eg 也失败了。但我们仍然可以让它发挥作用:

th> number = {}

th> function number:abs() return math.abs(self) end

th> x = -123

th> debug.setmetatable(x, {__index = number})
-123    

th> x:abs()
123 
Run Code Online (Sandbox Code Playgroud)

请注意,我们必须使用debug.setmetatable而不是setmetatable这里。两者之间的区别似乎是setmetatable仅为给定实例设置元表,而debug.setmetatable为整个类型设置元表。显然,为数字设置单独的元表是被禁止的(而且无论如何也没有多大意义)。这意味着(与表相比)新构造的数字现在默认具有给定的元表,因此这是有效的:

th> y = -42

th> y:abs()
42  
Run Code Online (Sandbox Code Playgroud)

[*] 更新

正如 Tom Blodget 所指出的,x:someFunc()如果x它本身用作命名空间,即它是一个带有方法字段的表,那么它也可以工作someFunc。例如你可以这样做table:insert(1)。但现在命名空间(名为 的表table)被传递为,self并且您将向命名空间添加数据:

th> print(getmetatable(table)) -- note: "table" does not have a metatable
nil 

th> table:insert(1) -- yet a colon syntax call works

th> table
{
  prune : function: 0x4156bde0
  getn : function: 0x41eb0720
  maxn : function: builtin#90
  remove : function: 0x41eb08c8
  foreachi : function: 0x41eb05b8
  sort : function: builtin#93
  concat : function: builtin#92
  unpack : function: builtin#16
  splice : function: 0x4156bdc0
  foreach : function: 0x41eb0688
  1 : 1
  pack : function: builtin#94
  insert : function: builtin#91
}
Run Code Online (Sandbox Code Playgroud)


Yu *_*Hao 5

关于string工作的第一个例子是因为,所有字符串共享相同的元表,并且它存储在名为的表中string.

来自string:

字符串库在表中提供其所有功能string.它还为__index字段指向表的字符串设置元string表.因此,您可以在面向对象的样式中使用字符串函数.例如,string.byte(s,i)可以写成s:byte(i).


第二个例子 table是行不通的,因为每个表都有自己的元表,命名的表table只是表库的所有函数的集合.


类似数字的类型默认情况下不支持metatable.