自定义运算符

lua-users home
wiki

Lua 具有一组预定义的运算符 [1],没有内置的能力在此基础上定义自定义运算符。尽管如此,还是有一些方法可以近似自定义运算符。这些解决方案中的许多都不太推荐。

技巧:用户定义的命名运算符 #1(中缀)

Lua 提供了一小组运算符,其中可以覆盖的运算符更少。<< 运算符,在 C++ 中常用于将对象插入输出流,不在此列。但这并不意味着我们不能尝试。下面的代码展示了一个我们可以模拟 Lua 中自定义运算符的定义和使用的方案。

-- Custom operator to evaluate (class)
local CustomOp = {}
function CustomOp:__div(b) -- eval full operation.
  return getmetatable(self.a)['__' .. self.op](self.a, b)
end
setmetatable(CustomOp, {__call =
  function(class, a, op) -- construct left-half of operation.
    return setmetatable({a = a, op = op}, CustomOp)
  end
})
function enable_custom_ops(mt) -- enable custom ops on metatable.
  function mt:__div(op)
    return CustomOp(self, op)
  end
  return mt
end

-- Output stream (class)
ostream  = {}
ostream.__index = ostream
enable_custom_ops(ostream)
function ostream:write(s)
  io.write(s)
end
ostream['__<<'] = function(self, s)  -- '<<' operator
  self:write(s)
  return self
end
setmetatable(ostream, {__call =
  function(class, file) -- construct output stream
    file = file or io.output()
    return setmetatable({file = file}, ostream)
  end
})
cout = ostream()
endl = "\n"  -- end of line

-- example usage

local _ = cout /'<<'/ "hello" /'<<'/ endl

--DavidManura, 200703

技巧:用户定义的命名运算符 #2(中缀)

这模仿了一个类似的 [Python 技巧]

local sm = setmetatable
local function infix(f)
  local mt = { __sub = function(self, b) return f(self[1], b) end }
  return sm({}, { __sub = function(a, _) return sm({ a }, mt) end })
end

local shl = infix(function(a, b) return a*(2^b) end)

print(5 -shl- 4)
--> 80

很巧妙,是吧?缺点:每次操作都分配一个表。

--MikePall, 2007-03-09

技巧:用户定义的命名运算符 #3

local function factorial(n)
  local y = 1
  for i=2,n do y = y * i end
  return y
end

local function xor(a,b)
  assert(a == 3 and b == 4) -- an exercise for the reader!
  return 7                  -- or see https://lua-users.lua.ac.cn/wiki/BitUtils
end

debug.setmetatable(0, {  -- create metatable for numbers
  __call = function(a, op)
   if op == '!' then return factorial(a)
   elseif op == 'xor' then return function(b) return xor(a,b) end
   end
  end
})

print(- (5)'!' + 1, - (3) 'xor' (4)) --> -119    -7

请注意运算符的优先级:这些实际上是函数调用。

对于一元后缀运算符,没有内存分配,但对于二元运算符有。我们可以如下改进(尽管不幸的是,这可能依赖于未定义的行为)

local savea
local function xorhelper(b)
  local a; a, savea = savea, nil
  return xor(a,b)
end
debug.setmetatable(0, {
  __call = function(a, op)
   if op == '!' then return factorial(a)
   elseif op == 'xor' then savea = a; return xorhelper
   end
  end
})

--DavidManura, 2007-07-14

另请参阅


RecentChanges · preferences
编辑 · 历史
最后编辑于 2009 年 5 月 1 日 下午 8:26 GMT (差异)