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

Lua 有一种语法糖,允许实现面向对象方法,当 : 用于 foo:bar(biz) 时,相当于 foo.bar(foo,biz)。然而,我发现标准字符串库也在以这种方式积极使用它:

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

问题是:这是如何实现的?当你试图假设它扩展为 a.reverse(a) 时,它就会分崩离析,因为 a 是一个字符串,不可能包含键reverse,此外,该语句实际上等价于 string.reverse(a)。我的猜想是它实际上扩展为 _G[type(a)].reverse(a),但是你会记得在文件描述符的 IO 库中也会出现相同的情况...

点赞
用户11740758
用户11740758

每个 Lua (5.1 - 5.4) 中的字符串都有一个带有元方法 __index 的元表。

这就是所有字符串函数存在的地方。

这是一个展示它的函数...

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

这非常简单,不是吗?

编辑:因为我忘了dump函数:-)...

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

这就是为什么它可以使用“.”和“:”作为方法的原因。

2021-04-09 17:56:45
用户459640
用户459640

冒号语法实际上等同于点语法加 self 参数。因此问题是,a.reverse 是如何运作的呢?

Lua 5.4 手册解释

字符串库提供了所有函数都在 string 表中。它还为字符串设置了一个元表,其中的 __index 字段指向 string 表。因此,您可以以面向对象的方式使用字符串函数。例如,string.byte(s,i) 可以写成 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)
-- 输出:gnirts lamron a

在线尝试!: 输出结果为

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
2021-04-09 17:59:37