Lua 内部:字符串方法如何作为方法工作?

Lua internals: how do string methods work as methods?

Lua 有一块允许实现 OOP 方法的语法糖:运算符 : 当用作 foo:bar(biz) 时等同于 foo.bar(foo,biz)。但是,我注意到标准字符串库也以这种方式积极使用它:

local a = "a normal string"
local b = a:reverse() -- ????????
print(b)
-- outputs: gnirts lamron a

问题是:这究竟是如何实现的?自然地,当您尝试假设它扩展为 a.reverse(a) 时它会分崩离析,因为 a 是一个字符串并且不可能包含键 reverse,此外,该语句实际上等同于 string.reverse(a)。我的直觉是它实际上扩展到 _G[type(a)].reverse(a),但你记得同样的事情发生在文件描述符的 io 库中......

Lua (5.1 - 5.4) 中的每个字符串都有一个带有元方法的元表 __index。
那是所有字符串函数都存在的地方。
这是一个显示它的函数...

-- help()
help=function(help)
dump(debug.getmetatable(help).__index)
end

这很简单,不是吗?

编辑:因为我忘记了转储功能:-) ...

-- dump(table)
dump=function(...)
local args={...}
local test,dump=pcall(assert,args[1])
if test then
for key,value in pairs(dump) do
  io.write(string.format("%s=%s\n",key,value))
  io.flush()
end
  return true
else
  return test,dump
end
end

有一个名为 _VERSION 的字符串 - 让我们看一下...

>help(_VERSION)
reverse = function: 0x565c4870
lower = function: 0x565c4ab0
sub = function: 0x565c7020
byte = function: 0x565c6d30
gsub = function: 0x565c7bb0
char = function: 0x565c4d80
format = function: 0x565c5060
unpack = function: 0x565c62d0
gmatch = function: 0x565c6ee0
dump = function: 0x565c5a90
upper = function: 0x565c47e0
find = function: 0x565c7ba0
len = function: 0x565c44d0
rep = function: 0x565c4900
pack = function: 0x565c66e0
packsize = function: 0x565c61c0
match = function: 0x565c7b90

这就是它作为方法与 .:

一起工作的原因

冒号语法真的等同于点语法加上 self 参数。所以问题是,a.reverse 是如何工作的?

The Lua 5.4 manual explains:

The string library provides all its functions inside the table string. It also sets a metatable for strings where the __index field points to the string table. Therefore, you can use the string functions in object-oriented style. For instance, string.byte(s,i) can be written as s:byte(i).

我们可以通过代码查看这些元表的详细信息:

local a = "a normal string"
strindex = getmetatable(a).__index
if type(strindex) == "table" then
  print "string has metatable entry __index:"
  for k,v in pairs(strindex) do
    local same = (v == string[k])
    print(k, type(v), "same as string."..k..":", same)
  end
end

local b = a.reverse(a) -- ????????
print(b)
-- outputs: gnirts lamron a

Try it online!:输出为

string has metatable entry __index:
rep function    same as string.rep: true
byte    function    same as string.byte:    true
sub function    same as string.sub: true
char    function    same as string.char:    true
gmatch  function    same as string.gmatch:  true
format  function    same as string.format:  true
reverse function    same as string.reverse: true
match   function    same as string.match:   true
dump    function    same as string.dump:    true
len function    same as string.len: true
packsize    function    same as string.packsize:    true
find    function    same as string.find:    true
unpack  function    same as string.unpack:  true
upper   function    same as string.upper:   true
pack    function    same as string.pack:    true
lower   function    same as string.lower:   true
gsub    function    same as string.gsub:    true
gnirts lamron a