自定义运算符

lua-users home
wiki

Lua 有一组预定义的运算符 [1],但没有内置的功能来定义超出此范围的自定义运算符。 尽管如此,还是有一些方法可以近似自定义运算符。 许多这些解决方案并不推荐。

Hack: 用户定义的命名运算符 #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

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

这模仿了类似的 [Python hack]

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

Hack: 用户定义的命名运算符 #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
edit · history
最后编辑于 2009 年 5 月 2 日凌晨 2:26 GMT (diff)