PGRData/Script/matrix/xformula/XArithmetic.lua

254 lines
No EOL
5.7 KiB
Lua

-- require("XCommon/XClass")
-- require("XCommon/XGlobalFunc")
local function split(input, delimiter)
input = tostring(input)
delimiter = tostring(delimiter)
if (delimiter=='') then return false end
local pos,arr = 0, {}
for st,sp in function() return string.find(input, delimiter, pos, true) end do
table.insert(arr, string.sub(input, pos, st - 1))
pos = sp + 1
end
table.insert(arr, string.sub(input, pos))
return arr
end
---------------------------------------------------------------
-- @class module
-- @name Stack
-- @author liqiang
local Stack = XClass(nil, "Stack")
--[[--
得到栈内元素数量
@treturn number size 元素个数
]]
function Stack:size()
return #self
end
--[[--
栈是否为空
@treturn boolean b true为空,否则为不空
]]
function Stack:empty()
return (self:size() == 0)
end
--[[--
出栈
@treturn void value 返回删除的元素,空返回nil
]]
function Stack:pop()
local tmp = self:top()
table.remove(self)
return tmp
end
--[[--
入栈
@tparam void value 入栈的元素
@treturn number size 入栈后元素个数
]]
function Stack:push(value)
table.insert(self, value)
return self:size()
end
--[[--
得到栈顶元素
@treturn void value 栈顶元素,空返回nil
]]
function Stack:top()
local size = self:size()
if size == 0 then
return nil
end
return self[size]
end
local XArithmetic = XClass(nil, "XArithmetic")
local defaultOperatorLevel = {
["+"] = 0,
["-"] = 0,
["("] = 10,
["*"] = 1,
["/"] = 1,
[")"] = 0,
["<"] = 1,
[">"] = 1,
["^"] = 2,
}
local defaultOperatorPattern = {
["%+"] = "+",
["%-"] = "-",
["%("] = "(",
["%*"] = "*",
["/"] = "/",
["%)"] = ")",
["<"] = "<",
[">"] = ">",
["%^"] = "^",
}
local charsReg = "^%a+%d*$"
function XArithmetic:Ctor()
self.OperatorLevel = defaultOperatorLevel
self.OperatorPattern = defaultOperatorPattern
self.GetVariableDelegate = nil
end
function XArithmetic:SetTextValueHandler(handle)
self.GetVariableDelegate = handle
end
function XArithmetic:Calculate(expression)
local rpnExperssion = self:ConvertToRPN(self:InsertBlank(expression))
return self:GetResultByExperssion(rpnExperssion)
end
function XArithmetic:GetValue(left, right, operator)
if "+" == operator then
return left + right
elseif "-" == operator then
return left - right
elseif "*" == operator then
return left * right
elseif "/" == operator then
return left / right
elseif "<" == operator then
return math.min(left, right)
elseif ">" == operator then
return math.max(left, right)
elseif "^" == operator then
return math.pow(left, right)
end
XLog.Warning("XArithmetic:GetValue error! operator: %s", operator)
return 0
end
function XArithmetic:GetResultByExperssion(source)
local operatorLevel = self.OperatorLevel
local stack = Stack.New()
local list = split(source, " ")
for i, current in ipairs(list) do
if tonumber(current) then
stack:push(tonumber(current))
elseif string.match(current, charsReg) then
stack:push(current)
elseif operatorLevel[current] then
local right = self:GetValueByText(stack:pop())
local left = self:GetValueByText(stack:pop())
stack:push(self:GetValue(left, right, string.sub(current, 1, 1)))
end
end
return stack:pop(), #stack
end
function XArithmetic:GetValueByText(text)
if self.GetVariableDelegate then
return self.GetVariableDelegate(text)
end
if tonumber(text) then
return tonumber(text)
end
XLog.Warning("XArithmetic.GetVariableDelegate == nil")
return 1
end
function XArithmetic:ConvertToRPN(source)
local operatorLevel = self.OperatorLevel
local result = ""
local stack = Stack.New()
local list = split(source, " ")
for i, current in ipairs(list) do
-- log("current %s", current)
if tonumber(current) then
result = result..current.." "
elseif string.match(current, charsReg) then
result = result..current.." "
elseif operatorLevel[current] then
if #stack > 0 then
local prev = stack:top()
-- log("prev %s", prev)
if prev == "(" then
stack:push(current)
elseif current == "(" then
stack:push(current)
elseif current == ")" then
while #stack > 0 and stack:top() ~= "(" do
result = result..stack:pop().." "
end
--Pop the "("
if #stack > 0 and stack:top() == "(" then
stack:pop()
end
elseif operatorLevel[current] <= operatorLevel[prev] then
while #stack > 0 do
local top = stack:pop()
if top ~= "(" and top ~= ")" and operatorLevel[current] <= operatorLevel[top] then
result = result..top.." "
else
-- break
stack:push(top)
break
end
end
stack:push(current)
else
stack:push(current)
end
else
stack:push(current)
end
end
local text = ""
local i = 1
while stack[i] ~= nil do
text = text.." "..stack[i]
i = i + 1
end
-- log(" "..text)
end
if #stack > 0 then
while #stack > 0 do
local top = stack:pop()
if top ~= "(" and top ~= ")" then
result = result..top.." "
end
end
end
-- log(result)
return result
end
function XArithmetic:InsertBlank(source)
for p, v in pairs(self.OperatorPattern) do
source = string.gsub(source, p, " "..v.." ")
end
return source
end
return XArithmetic
-- local function Test(expression, result)
-- local realyResult = XArithmetic:Calculate(expression);
-- XLog.Warning(realyResult, result)
-- if realyResult ~= result then
-- XLog.Warning("Formula.Calculate(\"%s\") != %d, realy result = %d", expression, result, realyResult)
-- else
-- XLog.Debug("XArithmetic test ok")
-- end
-- end
-- XLog.Warning("===========asdasd")
-- Test("4-2-1+3", 4)
-- Test("1+1*(2+1)-3.8/2 + 1 * (2+1)", 5.1)
-- Test("1000+0+0+0+0-0-0-0+0-100-0-0-0", 900)
-- Test("1000-0-100", 900)
-- Test("0-2*(3+4*6/5)", -15.6)