我能否覆盖Lua表的返回值?

3

如果没有键引用一个表,是否可能返回特定值而不是对它本身的引用?

假设我有以下表格:

local person = {
    name  = "Kapulani",
    level = 100,
    age   = 30,
}

在Lua中,我可以很容易地引用“person.name”,“person.level”或“person.age”,并获得预期的值。然而,我有某些情况可能只想引用“person”,而不是获取“table: ”,我希望返回“person.name”的值。
换句话说,我希望person.x(或person[x])返回表中适当的条目,但没有键的person返回person.name的值(或person["name"])。我是否能找到这样的机制?
我尝试过元表,但没有成功,因为__index仅适用于键不存在的情况。如果我将“person”放入单独的表中,则可以使用以下代码:
local true_person = {
    ... -- as above
}

local env_mt = {
    __index = function(t, k)
        if k == 'person' then
            return true_person
        end
    end
}

local env = setmetatable( {}, env_mt )

这让我可以使用__index进行一些特殊处理,但是从__index()无法区分是否正在请求env.person(我想返回true_person.name)或env.person[key](我想以表格形式返回true_person,以便适当地访问“ key”)。 有什么想法吗? 我可以采用不同的方法来处理,但希望能够沿着这些方向处理。

2
你正在努力克服Lua的语法,以创建自己的DSL。它真的提供了任何优势吗?举个例子会有所帮助。无论如何,你可以尝试使用__call元方法或者简单地为所有这样的表添加一个:default()方法,具有通用或特定于表的实现。 - Tom Blodget
我肯定正在权衡利弊。我一直在尝试使用 __call ,它更接近我所期望的,但我希望避免使用 () 的需求。我希望的优势是,在现有语法和Lua语法之间的“翻译”限制的情况下,当Lua抛出错误时,输出对用户来说相对有意义并且易于辨认。因此,将 & 转化为 and| 转化为 or 并不奇怪,但在某些地方将 person 转化为 person() 可能有点奇怪,而 person.name 不需要任何翻译。 - Kapulani
2个回答

2

当表格被用作字符串时,您可以通过设置__tostring元表条目来完成:

$ cat st.lua
local person = {
    name  = "Kapulani",
    level = 100,
    age   = 30,
}

print(person)
print(person.name)
print(person.age)

setmetatable(person, {__tostring = function(t) return t.name end})
print(person)

$ lua st.lua
lua st.lua
table: 0x1e8478e0
Kapulani
30
Kapulani

这非常有帮助和简单明了。如果默认值不是字符串而是数字怎么办?__tonumber似乎不是一个有效的元方法。是否有一种解决方法可以使用数值而不是字符串?例如,使用年龄而不是姓名? - Kapulani
2
@Kapulani,这是可以做到的,但我认为__tostring应该返回一个字符串,这是一个强有力的约定。 - Tom Blodget
1
元方法名称是正在执行的操作(和函数)的名称。您可以从该元方法返回数字。正如@TomBlodget所说,这可能有点奇怪,但在大多数情况下不会有任何问题(正常字符串用例将立即自动将数字转换为字符串)。唯一会受到影响的时候是如果有人直接在返回值上使用字符串元方法/等等。 - Etan Reisner
即使我劫持了 __tostring,我也必须在许多地方强制调用 **tostring()**(例如,如果我想让 person 返回 person.age 并且我正在进行 person > 21 比较)。 - Kapulani
我想我要放弃我的方法,但我会将其标记为答案,因为它在技术上实现了我最初的要求。感谢您提供的有益批评。 - Kapulani

1

我不确定你所要求的是否是一个好主意,因为它违背了组合性的原则。通常情况下,人们期望以下两个程序做同样的事情,但你希望它们行为不同。

print(person.name)

local p = person
print( p.name )

它也不是很清楚赋值如何工作。person.age = 10应该改变年龄,但是person = otherPerson应该改变对perrson的引用,而不是年龄。
如果您不关心组合性并且仅读取数据,则解决问题的更直接方法是具有接收以字符串编码的字段的查询函数。
query("person.age")   -- 17
query("person.name")  -- "hugomg"
query("person")       -- 17; query gets to default to whatever it wants.

为了使语法更轻便,您可以省略可选的括号。
q"person.age"
q"person"

或者你可以扩展全局表 _G 上的 __index 元方法。

setmetattable(_G, { __index = function(self, key) return query(key) end })

print ( person_age )  -- You will need to use "_" instead of "." for the
                      -- query to be a valid identifier.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接