Python学习笔记——改进调度场Shunting-yard算法,解析含有函数式的数学表达式

改进shunting-yard调度场算法,解析含有函数式的数学表达式

在最近的量化系统开发过程中,我遇到了一个问题,需要解析用户输入的数学/逻辑表达式,并计算这个表达式的结果。解析数学表达式的经典方法是所谓的调度场算法(Shunting-yard algorithm),将数学表达式由人类易读的“中缀表达式”重写为“后缀表达式”(逆波兰表达式),从而让计算机容易计算其结果。

不过,经典的调度场算法只能处理数学/逻辑运算符表达式,如果表达式中含有函数式,它是无能为力的,充其量可以处理单变量函数例如sqrt(), sin()等等。而我的工作恰恰需要解析含有函数式的表达式,而且函数的参数个数是不确定的,例如下面的表达式:
1 + s u m ( 1 , 2 , ( 3 + 5 ) ∗ 4 , m a x ( 3 , ( 4 + 5 ) ∗ 6 ) , 7 − 8 ) ∗ ( 2 + 3 ) 1+sum(1,2,(3+5)*4, max(3, (4+5)*6), 7-8) * (2+3) 1+sum(1,2,(3+5)4,max(3,(4+5)6),78)(2+3)
这个表达式中出现了两个函数,第一个sum()有5个参数,第二个max()有2个参数。为了实现对上述表达式的解析,需要改进标准的shunting-yard调度场算法,使它能在解析表达式的同时识别并存储函数的参数个数,从而实现对函数表达式的解析。

改进的Shunting-yard算法

本文的目的并不为了介绍经典的调度场算法,如果对本算法不熟悉的同学,建议阅读这篇文章

我假设这篇文章的读者本身已经研究过调度场算法,只是为了改进它以解析函数式,这里直接给出我改进后的调度场算法,其中改进的部分用高亮显示

算法使用一个队列,记为“输出队列”
算法使用两个栈,分别记为“op栈”和“args栈”,分别用于临时存放运算符和式子中函数的参数数量

  • 当还有记号可以读取时:
  • 读取一个记号。
  • 如果这个记号表示一个数字,那么将其添加到输出队列中。
  • 如果这个记号表示一个函数,那么将其压入op栈当中,同时将数字0压入args栈中
  • 如果这个记号表示一个函数参数的分隔符,例如一个半角逗号,那么:
    • op栈当中不断地弹出操作符并且放入输出队列中去,直到栈顶部的元素为一个左括号为止。如果一直没有遇到左括号,那么要么是分隔符放错了位置,要么是括号不匹配。
    • args栈最顶端的元素+1
  • 如果这个记号表示一个操作符,记做o1,那么:
    • 只要存在另一个记为o2的操作符位于op栈的顶端,并且
    • 如果o1是左结合性的并且它的运算符优先级要小于或者等于o2的优先级,或者
    • 如果o1是右结合性的并且它的运算符优先级比o2的要低,那么:
      • 将o2从op栈的顶端弹出并且放入输出队列中(循环直至以上条件不满足为止);
    • 然后,将o1压入op栈的顶端。
  • 如果这个记号是一个左括号,那么就将其压入op栈当中。
  • 如果这个记号是一个右括号,那么:
    • 从栈当中不断地弹出操作符并且放入输出队列中,直到运算符栈顶部的元素为左括号为止。
    • 将左括号从栈的顶端弹出并丢弃。
    • 如果此时位于op栈顶端的记号表示一个函数,那么:
      • args栈最顶端的元素+1,将元素弹出,记为arg_count
      • 将op栈顶端的函数弹出并放入输出队列中去,记录该函数的参数数量为arg_count
    • 如果在找到一个左括号之前栈就已经弹出了所有元素,那么就表示在表达式中存在不匹配的括号。
  • 当再没有记号可以读取时:
    • 如果此时在栈当中还有操作符:
      • 将操作符逐个弹出并放入输出队列中。
    • 如果此时位于栈顶端的操作符是一个括号,那么就表示在表达式中存在不匹配的括号。
  • 退出算法。

后缀表达式的计算

带函数式的后缀表达式的计算方式与通常的表达式一样,唯一的区别是在计算函数值的时候,需要读取该函数的参数数量,并且从数字栈中读取相应数量的参数带入函数式计算即可。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值