要实现支持运算优先级(如先乘除后加减)的计算器,可以使用逆波兰表达式(后缀表达式)算法或递归下降解析法。以下是易语言中的实现方案:
方案一:逆波兰表达式法(推荐)
.版本 2
.支持库 spec
.子程序 计算表达式, 小数型
.参数 表达式, 文本型
.局部变量 数字栈, 小数型, , "0"
.局部变量 运算符栈, 文本型, , "0"
.局部变量 i, 整数型
.局部变量 当前字符, 文本型
.局部变量 当前数字, 文本型
.局部变量 a, 小数型
.局部变量 b, 小数型
' 预处理表达式(去除空格)
表达式 = 子文本替换 (表达式, " ", "", , , 真)
.计次循环首 (取文本长度 (表达式), i)
当前字符 = 取文本中间 (表达式, i, 1)
' 处理数字
.如果真 (是否数字 (当前字符) 或 当前字符 = ".")
当前数字 = 当前数字 + 当前字符
.如果真 (i = 取文本长度 (表达式) 或 不是数字 (取文本中间 (表达式, i + 1, 1)))
加入成员 (数字栈, 到小数 (当前数字))
当前数字 = ""
.如果真结束
到循环尾 ()
.如果真结束
' 处理运算符优先级
.判断开始 (当前字符 = "(")
加入成员 (运算符栈, 当前字符)
.判断 (当前字符 = ")")
.判断循环首 (取数组成员数 (运算符栈) > 0 且 运算符栈 [取数组成员数 (运算符栈)] ≠ "(")
处理运算符 (数字栈, 运算符栈 [取数组成员数 (运算符栈)])
删除成员 (运算符栈, 取数组成员数 (运算符栈), 1)
.判断循环尾
删除成员 (运算符栈, 取数组成员数 (运算符栈), 1) ' 弹出左括号
.默认
.判断循环首 (取数组成员数 (运算符栈) > 0 且 优先级 (当前字符) ≤ 优先级 (运算符栈 [取数组成员数 (运算符栈)]))
处理运算符 (数字栈, 运算符栈 [取数组成员数 (运算符栈)])
删除成员 (运算符栈, 取数组成员数 (运算符栈), 1)
.判断循环尾
加入成员 (运算符栈, 当前字符)
.判断结束
.计次循环尾 ()
' 处理剩余运算符
.判断循环首 (取数组成员数 (运算符栈) > 0)
处理运算符 (数字栈, 运算符栈 [取数组成员数 (运算符栈)])
删除成员 (运算符栈, 取数组成员数 (运算符栈), 1)
.判断循环尾
返回 (数字栈 [1])
.子程序 处理运算符
.参数 数字栈, 小数型, 数组
.参数 运算符, 文本型
.局部变量 b, 小数型
.局部变量 a, 小数型
b = 数字栈 [取数组成员数 (数字栈)]
删除成员 (数字栈, 取数组成员数 (数字栈), 1)
a = 数字栈 [取数组成员数 (数字栈)]
删除成员 (数字栈, 取数组成员数 (数字栈), 1)
.判断开始 (运算符 = "+")
加入成员 (数字栈, a + b)
.判断 (运算符 = "-")
加入成员 (数字栈, a - b)
.判断 (运算符 = "*")
加入成员 (数字栈, a × b)
.判断 (运算符 = "/")
加入成员 (数字栈, a ÷ b)
.判断结束
.子程序 优先级, 整数型
.参数 op, 文本型
.判断开始 (op = "+" 或 op = "-")
返回 (1)
.判断 (op = "*" 或 op = "/")
返回 (2)
.默认
返回 (0)
.判断结束
.子程序 是否数字, 逻辑型
.参数 s, 文本型
返回 (寻找文本 ("0123456789.", s, , 假) > 0)
方案二:递归下降解析法(更直观)
.版本 2
.子程序 解析表达式, 小数型
.参数 表达式, 文本型
.局部变量 解析位置, 整数型
解析位置 = 1
返回 (解析加减 (表达式, 解析位置))
' 加减法处理(最低优先级)
.子程序 解析加减, 小数型
.参数 表达式, 文本型
.参数 解析位置, 整数型, 参考
.局部变量 结果, 小数型
.局部变量 运算符, 文本型
结果 = 解析乘除 (表达式, 解析位置)
.判断循环首 (取文本中间 (表达式, 解析位置, 1) = "+" 或 取文本中间 (表达式, 解析位置, 1) = "-")
运算符 = 取文本中间 (表达式, 解析位置, 1)
解析位置 = 解析位置 + 1
.判断开始 (运算符 = "+")
结果 = 结果 + 解析乘除 (表达式, 解析位置)
.判断 (运算符 = "-")
结果 = 结果 - 解析乘除 (表达式, 解析位置)
.判断结束
.判断循环尾
返回 (结果)
' 乘除法处理(中等优先级)
.子程序 解析乘除, 小数型
.参数 表达式, 文本型
.参数 解析位置, 整数型, 参考
.局部变量 结果, 小数型
.局部变量 运算符, 文本型
结果 = 解析基本单元 (表达式, 解析位置)
.判断循环首 (取文本中间 (表达式, 解析位置, 1) = "*" 或 取文本中间 (表达式, 解析位置, 1) = "/")
运算符 = 取文本中间 (表达式, 解析位置, 1)
解析位置 = 解析位置 + 1
.判断开始 (运算符 = "*")
结果 = 结果 × 解析基本单元 (表达式, 解析位置)
.判断 (运算符 = "/")
结果 = 结果 ÷ 解析基本单元 (表达式, 解析位置)
.判断结束
.判断循环尾
返回 (结果)
' 处理数字和括号(最高优先级)
.子程序 解析基本单元, 小数型
.参数 表达式, 文本型
.参数 解析位置, 整数型, 参考
.局部变量 当前字符, 文本型
.局部变量 数字文本, 文本型
当前字符 = 取文本中间 (表达式, 解析位置, 1)
.判断开始 (当前字符 = "(")
解析位置 = 解析位置 + 1
结果 = 解析加减 (表达式, 解析位置)
解析位置 = 解析位置 + 1 ' 跳过右括号
返回 (结果)
.默认
' 提取连续数字
.判断循环首 (是否数字 (当前字符) 或 当前字符 = ".")
数字文本 = 数字文本 + 当前字符
解析位置 = 解析位置 + 1
当前字符 = 取文本中间 (表达式, 解析位置, 1)
.判断循环尾
返回 (到小数 (数字文本))
.判断结束
使用示例
.子程序 _按钮计算_被单击
编辑框结果.内容 = 到文本 (计算表达式 ("23+34*45-16/6"))
' 输出:23+34*45-16/6 = 1549.333...
关键点说明
-
逆波兰表达式法:
- 通过维护两个栈(数字栈和运算符栈)处理优先级
- 时间复杂度O(n),适合复杂表达式
-
递归下降法:
- 通过多级子程序调用体现优先级
- 代码更易读,适合教学理解
-
优先级顺序:
括号 → 乘除 → 加减
-
扩展建议:
- 增加错误处理(如除零检查)
- 支持更多运算符(如^幂运算)
- 添加表达式合法性验证
如果需要更完整的实现(包括错误处理、科学计算等),可以进一步扩展上述框架。