Lua运算符重载

11
我发现一些网络上的地方说Lua运算符是可重载的,但我似乎找不到任何例子。
有人能提供一个例子吗?比如,重载+运算符使其像..运算符一样用于字符串连接?
编辑1:针对Alexander Gladysh和RBerteig的意见:
如果仅在两个操作数类型相同时才能使用运算符重载,并且更改此行为并不容易,那么以下代码为什么有效呢?(我不是要冒犯,我刚开始学这种语言。)
printf = function(fmt, ...)
    io.write(string.format(fmt, ...))
end

Set = {}
Set.mt = {}    -- metatable for sets

function Set.new (t)
    local set = {}
    setmetatable(set, Set.mt)
    for _, l in ipairs(t) do set[l] = true end
    return set
end


function Set.union (a,b)
    -- THIS IS THE PART THAT MANAGES OPERATOR OVERLOADING WITH OPERANDS OF DIFFERENT TYPES
    -- if user built new set using: new_set = some_set + some_number
    if type(a) == "table" and type(b) == "number" then
        print("building set...")
        local mixedset = Set.new{}
        for k in pairs(a) do mixedset[k] = true end
        mixedset[b] = true
        return mixedset
    -- elseif user built new set using: new_set = some_number + some_set
    elseif type(b) == "table" and type(a) == "number" then
        print("building set...")
        local mixedset = Set.new{}
        for k in pairs(b) do mixedset[k] = true end
        mixedset[a] = true
        return mixedset
    end

    if getmetatable(a) ~= Set.mt or
        getmetatable(b) ~= Set.mt then
        error("attempt to 'add' a set with a non-set value that is also not a number", 2)
    end

    local res = Set.new{}
    for k in pairs(a) do res[k] = true end
    for k in pairs(b) do res[k] = true end
    return res
end


function Set.tostring (set)
    local s = "{"
    local sep = ""
    for e in pairs(set) do
        s = s .. sep .. e
        sep = ", "
    end
    return s .. "}"
end

function Set.print (s)
    print(Set.tostring(s))
end

s1 = Set.new{10, 20, 30, 50}
s2 = Set.new{30, 1}

Set.mt.__add = Set.union

-- now try to make a new set by unioning a set plus a number:
s3 = s1 + 8
Set.print(s3)  --> {1, 10, 20, 30, 50}

1
这适用于比较运算符 - 而不是常规运算符。例如,重载以添加表和字符串是完全有效的。 - Puppy
2个回答

14

metatable函数仅适用于表格,但您可以使用debug.metatable来设置字符串的元表...

> mt = {}
> debug.setmetatable("",mt)
> mt.__add = function (op1, op2) return op1 .. op2 end
> ="foo"+"bar"
foobar
> 

另一种方法是使用 debug.getmetatable 来增强内置字符串元表(回答下面评论中的问题):

~ e$ lua
Lua 5.1.4  Copyright (C) 1994-2008 Lua.org, PUC-Rio
> debug.getmetatable("").__add = function (op1, op2) return op1 .. op2 end
> ="foo"+"bar"
foobar
> 

7
请注意,虽然这样做是可行的,但更改字符串元表可能是一种不好的实践。不要将语言适应你旧有的习惯 - 从长远来看,改变习惯总是更加有效。 - Alexander Gladysh
2
请注意,此示例替换了字符串的现有元表,通常是string模块,以便大多数字符串操作不需要全局查找。也可以使用debug.getmetatable检索现有元表并将__add方法添加到其中。正如亚历山大所说,这并不一定是一个好主意,但确实可行。 - RBerteig
1
@Alexander Gladysh:感谢您的建议,但我只是想看看Lua能做什么。我喜欢DSL(领域特定语言)的概念,所以我可能会滥用这些功能。 :) - PeterM
1
@DeadMG:如果您允许不受信任的代码更改非表类型的元表,那么您面临安全风险。 - Alexander Gladysh
1
例如,恶意代码可以更改本来安全的“系统级”Lua代码的行为。该代码可能具有对不安全函数的合法访问权限。这种攻击确实需要一些关于程序内部的知识,但仍然存在风险。 - Alexander Gladysh
显示剩余9条评论

6

请查看Lua编程手册的元表部分和Programming in Lua第二版的“元表和元方法”章节。

请注意,对于比较运算符的运算符重载,仅当两个操作数类型相同时才有效。


2
操作数类型的重要性无法强调得足够。这与许多其他支持重载的语言不同。而且若想改变它也不容易。 - RBerteig
谢谢提供链接。请参见上面的编辑1。 - PeterM
这只适用于比较运算符,对于通用运算符来说并不正确。你发布的链接已经过时了六年。 - Puppy
@DeadMG:嗯... 你可能是对的。我的脑海里还留有一些Lua 5.0的残余。请注意,Lua编程手册链接仍然有效——它是5.1手册。PiL链接指向过时的Lua 5.0版本,并可能包含过时的信息。 - Alexander Gladysh
@悲观主义者:请考虑购买第二版:http://www.inf.puc-rio.br/~roberto/pil2/ - Alexander Gladysh

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