跳转到内容

模組:Complex Number/Calculate

本页使用了标题或全文手工转换
被永久保护的模块
维基百科,自由的百科全书

这是本页的一个历史版本,由A2569875留言 | 贡献2018年11月8日 (四) 15:30 (prepare #invoke support of Module:Complex Number.)编辑。这可能和当前版本存在着巨大的差异。

(差异) ←上一修订 | 最后版本 (差异) | 下一修订→ (差异)

local p = {}
function p.calculate(frame)
    local args
    local can_math = false
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        args = frame.args
        local yesno = require('Module:Yesno')
        can_math = yesno(args['use math'] or args['use_math'])
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
    end
	local body = p.calc( args[1] or args['1'] or '', ( {
		cmath = require("Module:Complex Number").cmath.init(),
	} ) [ args['class'] ] , (( {
		cmath = require("Module:Complex Number").cmath.init(),
	} ) [ args['class'] ] ).toComplexNumber )
	if can_math then body = frame:callParserFunction{name = "#tag:math", args = {body}} end
	return body
end

function p.toPostfix(frame)
    local args
    if frame == mw.getCurrentFrame() then
        -- We're being called via #invoke. The args are passed through to the module
        -- from the template page, so use the args that were passed into the template.
        args = frame.args
    else
        -- We're being called from another module or from the debug console, so assume
        -- the args are passed in directly.
        args = frame
    end
	local format =  args['format'] or "{{{1}}} ";
	format = mw.ustring.gsub(format, "%{%{%{.-%}%}%}", "%%s" );
	local it = mw.ustring.find(format, "%%s", 1)
	format = mw.ustring.gsub(format, "\\n", "\n")
	local result, body = p.infixToPostfix( args[1] or args['1'] or '' ), ''
	for i, result_str in pairs( result ) do
		body = body .. mw.ustring.gsub(format, "%%s", tostring(result_str.name)) 
	end
	return body
end

function numberToAZ(num)
	local body = ''
	local s = tostring(math.floor(tonumber(num)))
	for i = 1, #s do
		body = body .. mw.ustring.char(tonumber(s:sub(i, i)) + 65)
	end
	return body
end

function stringToTable(s) --字串轉陣列
	local t, i = {}, 1
	while i <= #s do
		if s:sub(i, i) ~= ' ' then
			local j, k = mw.ustring.find(s,"[%d%a]+",i)
			if (j or (i+1)) > i then
				t[#t + 1] = s:sub(i, i)
			else
				t[#t + 1] = s:sub(j, k)
				i = k
			end
		end
		i = i + 1
	end
	return t
end

--mw.log(p.calc("45*5+(1+5-9+((12-5)+5i+(9)/4)*sqrt(-6))*-8",require("Module:Complex Number").cmath.init(),require("Module:Complex Number").cmath.init().toComplexNumber,true))
-- 346.97959 - 181.262241 i
--mw.log(p.calc("45*5+(1+5-9+((12-5)+5+(9)/4)*sin(-6))*-8",nil,nil,true))
-- 217.146633205
--mw.log(p.calc("(i*j*k)",p.qmath.init(),p.qmath.init().toQuaternionNumber,true))
-- (-1)
function p.calc(input_str, math_lib, number_Constructer, debug_flag)
	local mathlib, numberConstructer = math_lib or math, number_Constructer or tonumber
	local postfix = p.infixToPostfix(input_str, debug_flag)
	local call_stack = {}
	local calc_stack = {}
	local dbg_func = mw.log
	if debug_flag ~= true then dbg_func = function()end end
	for i = 1,#postfix do
		local it = postfix[i]
		if it.propetry == "operator" then
			local opdata = p.symbol_table[it.name]
			xpcall(function() 
				if it.name == ',' then
					if calc_stack[#calc_stack-1] ~= ',' then call_stack[#call_stack + 1] = calc_stack[#calc_stack-1] end 
					if calc_stack[#calc_stack] ~= ',' then call_stack[#call_stack + 1] = calc_stack[#calc_stack] end 
					calc_stack[#calc_stack] = nil; calc_stack[#calc_stack] = ','
				elseif opdata.count == 1 then
					calc_stack[#calc_stack] = opdata.calc(calc_stack[#calc_stack],mathlib,numberConstructer)
				else
					calc_stack[#calc_stack - 1] = opdata.calc(calc_stack[#calc_stack - 1]or 0 ,calc_stack[#calc_stack]or 0,mathlib,numberConstructer)
					calc_stack[#calc_stack] = nil
				end
			end, function(message)
				error("計算失敗:套用運算子 \"" .. mw.text.trim(it.name) .. "\" 發生錯誤 \"" .. message .. "\" .")
			end)
			xpcall(function() (calc_stack[#calc_stack]):clean() end,function(_)end)
			dbg_func(it.name, '\t', print_sk(calc_stack,', ') )
		elseif it.propetry == "func" then
			local calfunc, functype = mathlib[it.name], type(tonumber)
			if type(calfunc) ~= functype and type(_G[it.name]) == functype then calfunc = _G[it.name] end
			if type(calfunc) == functype then
				xpcall(function() 
					if #call_stack > 0 then
						calc_stack[#calc_stack] = calfunc( unpack( call_stack ) )
					else
						calc_stack[#calc_stack] = calfunc( calc_stack[#calc_stack] )
					end
				end, function(message)
					error("計算失敗:執行函數 \"" .. it.name .. "\" 發生錯誤 \"" .. message .. "\" .")
				end)
			else
				error("計算失敗:無法執行函數 \"" .. it.name .. "\" ")
			end
			xpcall(function() (calc_stack[#calc_stack]):clean() end,function(_)end)
			dbg_func(it.name, '\t', print_sk(calc_stack,', ') )
		else
			local get_data = numberConstructer(it.name)
			if get_data == nil then get_data = numberConstructer(_G[it.name]) or numberConstructer(mathlib[it.name]) or numberConstructer(it.name) end
			if get_data == nil then error("計算失敗:未知的變數 \"" .. it.name .. "\" ") end
			calc_stack[#calc_stack + 1] = get_data
			xpcall(function() (calc_stack[#calc_stack]):clean() end,function(_)end)
			dbg_func(it.name, '\t', print_sk(calc_stack,', ') )
		end
	end
	if #calc_stack == 1 then return calc_stack[#calc_stack] end
	return numberConstructer(0)
end

p.symbol_table = {
		['+'] = { propetry="op", multp = true, count = 2, priority=2, calc=function(a,b,c,d)return d(a)+d(b) end},
		["+ "] = { propetry="op", count = 1, priority=5, calc=function(a,c,d)return d(a) end},
		['-'] = { propetry="op", multp = true, count = 2, priority=2, calc=function(a,b,c,d)return d(a)-d(b) end},
		["- "] = { propetry="op", count = 1, priority=5, calc=function(a,c,d)return -d(a) end},
		['*'] = { propetry="op", multp = true, count = 2, priority=3, calc=function(a,b,c,d)return d(a)*d(b) end},
		["* "] = { propetry="op", count = 1, priority=5 , calc=function(a,c,d) if type(c.conjugate)==type(function()end) then return c.conjugate(d(a))else return d(a)end end},
		['/'] = { propetry="op", count = 2, priority=3, calc=function(a,b,c,d)return d(a)/d(b) end},
		['%'] = { propetry="op", count = 2, priority=3, calc=function(a,b,c,d)return d(a)%d(b) end},
		['^'] = { propetry="op", count = 2, priority=4, calc=function(a,b,c,d)return c.pow(d(a),d(b)) end},
		[','] = { propetry="op", count = 2, priority=1 },
}

	function print_sk(sk,sp)
		local body = ''
		for i = 1,#sk do
			if body ~= '' then body = body .. (sp or ' ') end
			if type(sk[i]) == type({}) and sk[i].name then if sk[i].name == "$END" then body = body .. '$' else body = body .. sk[i].name end
			else body = body .. tostring(sk[i]) end
		end
		return body
	end

function p.infixToPostfix(input_str, debug_flag)
	local str, index, num_list = input_str, 0, {}; 
	local dbg_func = mw.log
	if debug_flag ~= true then dbg_func = function()end end
	str = mw.ustring.gsub(str,"([A-Za-hj-z])i","%1I")
	str = mw.ustring.gsub(str,"([A-Za-ik-z])j","%1J")
	str = mw.ustring.gsub(str,"([A-Za-jl-z])k","%1K")
	str = mw.ustring.gsub(str,"i([A-Za-hj-z])","I%1")
	str = mw.ustring.gsub(str,"j([A-Za-ik-z])","J%1")
	str = mw.ustring.gsub(str,"k([A-Za-jl-z])","K%1")
	local result = mw.ustring.gsub(str,"%d*%.?%d*%s*[ijk]?",
		function(b) 
			if b ~= nil and mw.text.trim(b) ~= '' then 
				index = index + 1 
				num_list[#num_list + 1] = b 
				return tostring(index) 
			end 
			return ''
		end 
	); 
	result = mw.ustring.gsub(result,"I","i")
	result = mw.ustring.gsub(result,"J","j")
	result = mw.ustring.gsub(result,"K","k")
	--------------------------------------------------------
	local str2, index, azid, func_list = result, 0, '', {}; 
	local result = mw.ustring.gsub(str2,"%a[%a%d]*%s*%(?",
		function(b) 
			if b ~= nil and mw.text.trim(b) ~= '' then 
				if mw.ustring.find(b,"%(") then
					local func_name = mw.ustring.match(b,"%a[%a%d]*")
					if func_name ~= nil and mw.text.trim(func_name) ~= '' then  
						index, azid = index + 1, numberToAZ(index)
						func_list[azid] = func_name 
						return ' ' .. azid .. ' '
					end
				else
					num_list[#num_list + 1] = b 
					return tostring(#num_list) 
				end
			end 
			return ''
		end 
	); 
	for i=1,#num_list do num_list[i] = mw.ustring.gsub(num_list[i],"%s+",'') end
	
	local mid_expr = stringToTable(result)
	mid_expr[#mid_expr + 1] = "$END"
	local stack, postfix = {{name="$END",elements=0}}, {}
	for i = 1,#mid_expr do
		local it = mid_expr[i]
		if it == "$END" then
			while #stack > 0 and stack[#stack].name ~= "$END"  do
				if stack[#stack].elements < p.symbol_table[stack[#stack].name].count then
					dbg_func("==operator ",stack[#stack].name, " need ",p.symbol_table[stack[#stack].name].count, " elements, but got ",stack[#stack].elements)
					return {}
				end
				postfix[#postfix + 1] = {name=stack[#stack].name, propetry=stack[#stack].propetry}
				stack[#stack] = nil --pop
				stack[#stack].elements = (stack[#stack].elements or 0) + 1
			end
			dbg_func("結束",'\t', print_sk(postfix,' ') ,'\t',print_sk(stack))
		elseif mw.ustring.match(it,"[%a%(]") then
			stack[#stack + 1] = {name=it,elements=0,propetry='func'}
			dbg_func(it, "括號開始",'\t', print_sk(postfix,' ') ,'\t',print_sk(stack))
		elseif p.symbol_table[it] ~= nil and p.symbol_table[it].propetry == "op" then
			local op_it = p.symbol_table[it]
			local op_ls = p.symbol_table[mid_expr[i-1]]
			local flag = mw.ustring.match(mid_expr[i-1] or '',"[%a%(]") if (mid_expr[i-1] or '娜娜奇') == '娜娜奇' then flag = false end
			if ( op_ls or (i == 1) or flag ) and op_it.multp == true then
				stack[#stack + 1] = {name=it .. ' ',elements=0,propetry='operator'}
			else
				while p.symbol_table[stack[#stack].name] and p.symbol_table[stack[#stack].name].priority and
					p.symbol_table[stack[#stack].name].priority >= p.symbol_table[it].priority do
						if stack[#stack].elements < p.symbol_table[stack[#stack].name].count then
							dbg_func("==operator ",stack[#stack].name, " need ",p.symbol_table[stack[#stack].name].count, " elements, but got ",stack[#stack].elements)
							return {}
						end
					postfix[#postfix + 1] = {name=stack[#stack].name, propetry=stack[#stack].propetry}
					stack[#stack] = nil --pop
					stack[#stack].elements = (stack[#stack].elements or 0) + 1
				end
				stack[#stack + 1] = {name=it,elements=1, propetry='operator'}
			end
			dbg_func(it, "運算子",'\t', print_sk(postfix,' ') ,'\t',print_sk(stack))
		elseif mw.ustring.match(it,"%d+") then
			postfix[#postfix + 1] = {name=num_list[tonumber(it)] or ("N" .. it), propetry="number"}
			stack[#stack].elements = (stack[#stack].elements or 0) + 1
			dbg_func(it, "數字",'\t', print_sk(postfix,' ') ,'\t',print_sk(stack))
		elseif it == ')' then
			local flag = mw.ustring.match(stack[#stack].name,"[%a%(]")
			while flag == nil do --遇 ) 輸出至 ( 
				if stack[#stack].elements < p.symbol_table[stack[#stack].name].count then
					dbg_func("==operator ",stack[#stack].name, " need ",p.symbol_table[stack[#stack].name].count, " elements, but got ",stack[#stack].elements)
					return {}
				end
				postfix[#postfix + 1] = {name=stack[#stack].name, propetry=stack[#stack].propetry}
				stack[#stack] = nil --pop
				stack[#stack].elements = (stack[#stack].elements or 0) + 1
				flag = mw.ustring.match(stack[#stack].name,"[%a%(]")
			end
			if mw.ustring.match(stack[#stack].name,"%a") then
				postfix[#postfix + 1] = {name=func_list[stack[#stack].name] or stack[#stack].name, propetry=stack[#stack].propetry}
			end
			stack[#stack] = nil --pop
			stack[#stack].elements = (stack[#stack].elements or 0) + 1
			dbg_func(it, "結束括號",'\t', print_sk(postfix,' ') ,'\t',print_sk(stack))
		end
	end
	return postfix

end
return p