虽然我理解和之间.:的基本区别,但我还没有完全弄清楚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()?
作为一个完全的Lua新手,我花了一段时间才理解@Yuhao的回答,所以我会尝试为其他初学者添加一些细节。如果有什么不对的地方请指正。
据我所知,像这样的调用x:someFunc()有效 if [*]:
x有一个元表__indexsomeFunc。正如于浩所指出的,字符串会自动获得一个指向表的元表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)