[批处理][算法实战007]逆波兰四则计算器
特点:计算精确无误差。例:(3*3*3/2)/((-9)/(11111*3*(1-99998/99999)))
VBS计算结果:-.499999999998098
该程序计算结果:-1/2
逆波兰四则计算器.bat::转载请注明出处。
@Echo Off
Setlocal Enabledelayedexpansion
::CODER BY 老刘 POWERD BY iBAT
Title 【老刘编写】逆波兰四则计算器
Path "%~dp0"
If Not Exist "Stack(Linked Storage Structure).Bat" (
Echo "Stack(Linked Storage Structure)"缺失!
Pause&Exit
)
Set "Stack=Call "Stack(Linked Storage Structure)""
!Stack! :Init 运算符
!Stack! :Init 操作数
Echo Code By OldLiu
Echo.
Echo 表达式中只能出现“0123456789+-*/()”这些字符。
Echo 计算支持分数、括号可以嵌套。
Echo 支持负数,但负号前有运算符时需加括号。
Echo 错误举例:6/-3 5--3*-8
Echo 正确举例:6/(-3) 5-(-3*(-8))
Echo 输入Quit或Exit退出。
Echo.
Set /p IsDebug=是否开启DEBUG模式(y/n):
If /i "!IsDebug!" Neq "y" (
Set "Hide=Rem"
) Else (
Set "Hide="
)
:Loop
Echo. & Set /p "中缀表达式=键入表达式>>> "
If /i "!中缀表达式!" Equ "Quit" Exit
If /i "!中缀表达式!" Equ "Exit" Exit
Rem 中缀转后缀。
!Stack! :Clear 运算符
Set 后缀表达式=
Set 可能出现负数=True
:读取中缀表达式
If "!中缀表达式!" == "" Goto 中缀转后缀完成
Set "字符=!中缀表达式:~,1!"
%Hide% Set 中缀表达式
Set "中缀表达式=!中缀表达式:~1!"
Rem 处理数字。
Set /A "IsNum=!字符!" 2>Nul
If "!IsNum!" EQU "!字符!" (
Set 数字=!数字!!字符!
Set 可能出现负数=False
Goto 读取中缀表达式
) Else (
If Defined 数字 (
Set "后缀表达式=!后缀表达式! !数字!"
%Hide% Set 后缀表达式
Set 数字=
)
)
Rem 处理左括弧。
If "!字符!" == "(" (
Rem 左括弧不在栈中时优先级最高,直接压栈。
!Stack! :Push 运算符 字符
Set 可能出现负数=True
Goto 读取中缀表达式
)
Rem 处理右括弧。
If "!字符!" == ")" (
Rem 运算符出栈,直到遇到左括弧。
:括弧中运算符输出
!Stack! :Pop 运算符 栈顶运算符
If "!栈顶运算符!" Neq "(" (
Set "后缀表达式=!后缀表达式! !栈顶运算符!"
%Hide% Set 后缀表达式
Goto 括弧中运算符输出
)
Goto 读取中缀表达式
)
Rem 处理运算符。
If "!字符!" == "-" (
If !可能出现负数! == True (
Rem “-”号前面没数字,补一个“0”避免后缀表达式运算错误。
Set "后缀表达式=!后缀表达式! 0"
%Hide% Set 后缀表达式
)
)
!Stack! :IsEmpty 运算符
If !ErrorLevel! Equ 0 (
Rem 栈是空的,任何运算符优先级高于空栈,入栈。
!Stack! :Push 运算符 字符
Goto 读取中缀表达式
) Else (
:运算符优先级比对
!Stack! :Pop 运算符 栈顶运算符
If !ErrorLevel! Equ 0 (
Set 优先级高=False
Rem 一切运算优先级高于栈中的左括弧。
If "!栈顶运算符!" == "(" Set 优先级高=True
Rem 乘除法优先级高于加减法。
If "!栈顶运算符!" == "+" (
If "!字符!" == "*" Set 优先级高=True
If "!字符!" == "/" Set 优先级高=True
)
If "!栈顶运算符!" == "-" (
If "!字符!" == "*" Set 优先级高=True
If "!字符!" == "/" Set 优先级高=True
)
If "!优先级高!" == "True" (
Rem 当前运算优先级高于栈顶运算优先级,入栈。
!Stack! :Push 运算符 栈顶运算符
!Stack! :Push 运算符 字符
Goto 读取中缀表达式
) Else (
Rem 当前运算优先级不高于栈顶运算优先级,栈顶的运算可以计算了。
Set "后缀表达式=!后缀表达式! !栈顶运算符!"
%Hide% Set 后缀表达式
Rem 继续出栈,直到栈空或当前运算优先级高于栈顶运算。
Goto 运算符优先级比对
)
) Else (
Rem 栈已经清空,任何运算符优先级高于空栈,入栈。
!Stack! :Push 运算符 字符
Goto 读取中缀表达式
)
Echo 输入的表达式有误。
Goto Loop
)
:中缀转后缀完成
Rem 写入最后一个数字。
Set "后缀表达式=!后缀表达式! !数字!"
Set 数字=
Rem 弹出栈中所有运算符。
!Stack! :Pop 运算符 栈顶运算符
:运算符弹出
Set "后缀表达式=!后缀表达式! !栈顶运算符!"
!Stack! :Pop 运算符 栈顶运算符
If !ErrorLevel! Equ 0 Goto 运算符弹出
%Hide% Set 后缀表达式
Rem 开始计算结果。
!Stack! :Clear 操作数
Call :计算结果 !后缀表达式!
Goto 计算完成
:计算结果
Set 字符=%1
Set /A "IsNum=!字符!" 2>Nul
If "!IsNum!" EQU "!字符!" (
Rem 数字入栈。
!Stack! :Push 操作数 字符
) Else (
Rem 遇到运算符,计算。
Rem 从栈中弹出操作数,注意操作数2先出来。
!Stack! :Pop 操作数 操作数2
If !ErrorLevel! Neq 0 (
Echo 输入的表达式有误。
Goto Loop
)
!Stack! :Pop 操作数 操作数1
If !ErrorLevel! Neq 0 (
Echo 输入的表达式有误。
Goto Loop
)
Rem 整数转为分数。
If "!操作数1:/=!" Equ "!操作数1!" (
Set "操作数1=!操作数1!/1"
)
If "!操作数2:/=!" Equ "!操作数2!" (
Set "操作数2=!操作数2!/1"
)
Rem 进行运算。
If "!字符!" Equ "*" (
Rem 分子互乘、分母互乘。
For /f "tokens=1,2 delims=/" %%a in ("!操作数1!") do (
Set /A 当前结果分子=%%a
Set /A 当前结果分母=%%b
)
For /f "tokens=1,2 delims=/" %%a in ("!操作数2!") do (
Set /A 当前结果分子*=%%a
Set /A 当前结果分母*=%%b
)
)
If "!字符!" Equ "/" (
Rem 除以一个数等于乘其倒数。
For /f "tokens=1,2 delims=/" %%a in ("!操作数1!") do (
Set /A 当前结果分子=%%a
Set /A 当前结果分母=%%b
)
For /f "tokens=1,2 delims=/" %%a in ("!操作数2!") do (
Set /A 当前结果分子*=%%b
Set /A 当前结果分母*=%%a
)
)
Set 加减法=False
If "!字符!" Equ "+" Set 加减法=True
If "!字符!" Equ "-" Set 加减法=True
If "!加减法!" Equ "True" (
Rem 母互乘子,并以为实,母相乘为法,实如法而一。
For /f "tokens=1,2 delims=/" %%a in ("!操作数1!") do (
Set /A 当前结果分子=%%a
Set /A 当前结果分母=%%b
)
For /f "tokens=1,2 delims=/" %%a in ("!操作数2!") do (
Set /A 当前结果分子=当前结果分子*%%b!字符!当前结果分母*%%a
Set /A 当前结果分母*=%%b
)
)
Rem 分数化简。
Set /A 被除数=当前结果分子,除数=当前结果分母
:求最大公约数
If !除数! Equ 0 (
Echo 除以0错误。
Goto Loop
)
Set /A 余数=被除数%%除数
If !余数! Neq 0 (
Set /A 被除数=除数,除数=余数
Goto 求最大公约数
)
Rem 结果约分。
Set /A 当前结果分子/=除数,当前结果分母/=除数
Set 当前结果=!当前结果分子!/!当前结果分母!
%Hide% Echo ^(!操作数1!^)!字符!^(!操作数2!^)=!当前结果!
!Stack! :Push 操作数 当前结果
)
Shift /1
If "%1" Neq "" Goto 计算结果
Goto :Eof
:计算完成
!Stack! :Pop 操作数 计算结果
For /f "tokens=1,2 delims=/" %%a in ("!计算结果!") do (
Set /A 结果分子=%%a,结果分母=%%b
If !结果分母! Lss 0 Set /A 结果分子*=-1,结果分母*=-1
If !结果分子! Equ 0 (
Echo 结果:0
) Else If !结果分母! Equ 1 (
Rem 结果为非0整数。
Echo 结果:!结果分子!
) Else If !结果分子! Gtr !结果分母! (
Rem 结果绝对值大于1。
Set /A 商=结果分子/结果分母,余数=%%a%%结果分母
Echo 结果:!结果分子!/!结果分母!=!商!…!余数!
) Else (
Rem 结果绝对值小于1。
Echo 结果:!结果分子!/!结果分母!
)
)
Goto LoopStack(Linked Storage Structure).BAT::Code By OldLiu
Call %*
Goto :Eof
:GetRandom
Set /A ErrorLevel=%random%%%1000+%random%*10000
If Defined Memory[!ErrorLevel!].Data Goto GetRandom
Goto :Eof
:Init StackName
Call :GetRandom
Set /A %~1=ErrorLevel
Set /A Memory[!ErrorLevel!].Data=0
Set /A Memory[!ErrorLevel!].Next=0
Set /A ErrorLevel=0
Goto :Eof
:IsEmpty StackName
:GetLength StackName
Set /A ErrorLevel=Memory[!%~1!].Data
Goto :Eof
:Clear StackName
Set /A _TMP_Length_=Memory[!%~1!].Data
Set /A Memory[!%~1!].Data=0
Set /A _TMP_Next_Node_Ptr_=Memory[!%~1!].Next
Set /A Memory[!%~1!].Next=0
For /L %%a In (1 1 !_TMP_Length_!) Do (
Set /A _TMP_This_Node_Ptr_=_TMP_Next_Node_Ptr_
Set /A _TMP_Next_Node_Ptr_=Memory[!_TMP_This_Node_Ptr_!].Next
Set Memory[!_TMP_This_Node_Ptr_!].Data=
Set Memory[!_TMP_This_Node_Ptr_!].Next=
)
Set _TMP_This_Node_Ptr_=
Set _TMP_Next_Node_Ptr_=
Set _TMP_Length_=
Set /A ErrorLevel=0
Goto :Eof
:Push StackName VarToPush
Set /A Memory[!%~1!].Data+=1
Call :GetRandom
Set "Memory[!ErrorLevel!].Data=!%~2!"
Set /A Memory[!ErrorLevel!].Next=Memory[!%~1!].Next
Set /A Memory[!%~1!].Next=ErrorLevel
Set /A ErrorLevel=0
Goto :Eof
:GetTopElem StackName VarToReturn
Set /A _TMP_Node_Ptr_=Memory[!%~1!].Next
If "!_TMP_Node_Ptr_!" Equ "0" (
Set /A ErrorLevel=1
Goto :Eof
)
Call Set "%~2=%%Memory[!_TMP_Node_Ptr_!].Data%%"
Set _TMP_Node_Ptr_=
Set /A ErrorLevel=0
Goto :Eof
:Pop StackName VarToReturn
Set /A _TMP_Node_Ptr_=Memory[!%~1!].Next
If "!_TMP_Node_Ptr_!" Equ "0" (
Set /A ErrorLevel=1
Goto :Eof
)
Call Set "%~2=%%Memory[!_TMP_Node_Ptr_!].Data%%"
Set "Memory[!_TMP_Node_Ptr_!].Data="
Set /A Memory[!%~1!].Next=Memory[!_TMP_Node_Ptr_!].Next
Set "Memory[!_TMP_Node_Ptr_!].Next="
Set /A Memory[!%~1!].Data-=1
Set _TMP_Node_Ptr_=
Set /A ErrorLevel=0
Goto :Eof
:EditTopElem StackName VarToReplaceWith
Set /A _TMP_Node_Ptr_=Memory[!%~1!].Next
If "!_TMP_Node_Ptr_!" Equ "0" (
Set /A ErrorLevel=1
Goto :Eof
)
Set "Memory[!_TMP_Node_Ptr_!].Data=!%~2!"
Set _TMP_Node_Ptr_=
Set /A ErrorLevel=0
Goto :Eof
:Delete StackName
Call :Clear
Set "Memory[!%~1!].Data="
Set "Memory[!%~1!].Next="
Set /A ErrorLevel=0
Goto :Eof
计算测试(DEBUG模式开)键入表达式>>> (3*3*3/2)/((-9)/(11111*3*(1-99998/99999)))
中缀表达式=(3*3*3/2)/((-9)/(11111*3*(1-99998/99999)))
中缀表达式=3*3*3/2)/((-9)/(11111*3*(1-99998/99999)))
中缀表达式=*3*3/2)/((-9)/(11111*3*(1-99998/99999)))
后缀表达式= 3
中缀表达式=3*3/2)/((-9)/(11111*3*(1-99998/99999)))
中缀表达式=*3/2)/((-9)/(11111*3*(1-99998/99999)))
后缀表达式= 3 3
后缀表达式= 3 3 *
中缀表达式=3/2)/((-9)/(11111*3*(1-99998/99999)))
中缀表达式=/2)/((-9)/(11111*3*(1-99998/99999)))
后缀表达式= 3 3 * 3
后缀表达式= 3 3 * 3 *
中缀表达式=2)/((-9)/(11111*3*(1-99998/99999)))
中缀表达式=)/((-9)/(11111*3*(1-99998/99999)))
后缀表达式= 3 3 * 3 * 2
后缀表达式= 3 3 * 3 * 2 /
中缀表达式=/((-9)/(11111*3*(1-99998/99999)))
中缀表达式=((-9)/(11111*3*(1-99998/99999)))
中缀表达式=(-9)/(11111*3*(1-99998/99999)))
中缀表达式=-9)/(11111*3*(1-99998/99999)))
后缀表达式= 3 3 * 3 * 2 / 0
中缀表达式=9)/(11111*3*(1-99998/99999)))
中缀表达式=)/(11111*3*(1-99998/99999)))
后缀表达式= 3 3 * 3 * 2 / 0 9
后缀表达式= 3 3 * 3 * 2 / 0 9 -
中缀表达式=/(11111*3*(1-99998/99999)))
中缀表达式=(11111*3*(1-99998/99999)))
中缀表达式=11111*3*(1-99998/99999)))
中缀表达式=1111*3*(1-99998/99999)))
中缀表达式=111*3*(1-99998/99999)))
中缀表达式=11*3*(1-99998/99999)))
中缀表达式=1*3*(1-99998/99999)))
中缀表达式=*3*(1-99998/99999)))
后缀表达式= 3 3 * 3 * 2 / 0 9 - 11111
中缀表达式=3*(1-99998/99999)))
中缀表达式=*(1-99998/99999)))
后缀表达式= 3 3 * 3 * 2 / 0 9 - 11111 3
后缀表达式= 3 3 * 3 * 2 / 0 9 - 11111 3 *
中缀表达式=(1-99998/99999)))
中缀表达式=1-99998/99999)))
中缀表达式=-99998/99999)))
后缀表达式= 3 3 * 3 * 2 / 0 9 - 11111 3 * 1
中缀表达式=99998/99999)))
中缀表达式=9998/99999)))
中缀表达式=998/99999)))
中缀表达式=98/99999)))
中缀表达式=8/99999)))
中缀表达式=/99999)))
后缀表达式= 3 3 * 3 * 2 / 0 9 - 11111 3 * 1 99998
中缀表达式=99999)))
中缀表达式=9999)))
中缀表达式=999)))
中缀表达式=99)))
中缀表达式=9)))
中缀表达式=)))
后缀表达式= 3 3 * 3 * 2 / 0 9 - 11111 3 * 1 99998 99999
后缀表达式= 3 3 * 3 * 2 / 0 9 - 11111 3 * 1 99998 99999 /
后缀表达式= 3 3 * 3 * 2 / 0 9 - 11111 3 * 1 99998 99999 / -
中缀表达式=))
后缀表达式= 3 3 * 3 * 2 / 0 9 - 11111 3 * 1 99998 99999 / - *
中缀表达式=)
后缀表达式= 3 3 * 3 * 2 / 0 9 - 11111 3 * 1 99998 99999 / - * /
后缀表达式= 3 3 * 3 * 2 / 0 9 - 11111 3 * 1 99998 99999 / - * //
(3/1)*(3/1)=9/1
(9/1)*(3/1)=27/1
(27/1)/(2/1)=27/2
(0/1)-(9/1)=-9/1
(11111/1)*(3/1)=33333/1
(99998/1)/(99999/1)=99998/99999
(1/1)-(99998/99999)=1/99999
(33333/1)*(1/99999)=1/3
(-9/1)/(1/3)=-27/1
(27/2)/(-27/1)=1/-2
结果:-1/2 这特么真是高手,我操 真是将bat功能发挥到极致 太感谢了,正想学习这方面的知识呢。谢谢分享!
页:
[1]