公式解析器 与 RPN
本文最后更新于 130 天前,其中的信息可能已经有所发展或是发生改变。

突然发现写的这玩意,可能和单片机写的小计算器有关系,试了试好像还真的行得通,顺便写一下笔记

概念

相比于解析复杂的中缀表达式(中缀的实现需要双栈/递归),RPN更加符合计算机直觉,只需一个栈即可计算。利用 Shunting-yard 算法,将 RPN 解析为 中缀,对用户更加友好

逆波兰表达式(RPN):即后缀表达式。不需要括号,就能明确运算顺序的表达式的表示法。他的特点是:

  • 运算符在两个数的后面
  • 完全没有括号
  • 运算顺序完全由运算符和数的 位置 决定

我们平时使用的是中缀表达式:(1 + 2) * 3。而 RPN 是: 1 2 + 3 *

算法实现

  1. 先创建一个空栈,用于存储。
  2. 然后,遍历所有标记,拿到 操作数 运算符
  3. 遇到数字,直接压入栈中
  4. 遇到运算符了,从 栈顶 弹出 操作数 ,再压回栈中。然后完成了基本的RPN计算!
  5. 当结果的栈长度不是1!那就是你太杂鱼把表达式敲错啦,回去再练练吧ww
private static evaluateRPN(tokens: (number | string)[]): number {
  const stack: number[] = [];
  for (const token of tokens) {
    if (typeof token === "number") {
      stack.push(token);
    } else if (this.isOperator(token)) {
      const b = stack.pop()!;
      const a = stack.pop()!;
      stack.push(this.evaluateOperator(token, a, b));
    }
  }
  if (stack.length !== 1) {
    throw new TypeError("居然打出了非法表达式,真是个杂鱼♡");
  }
  return stack[0]; // 大功告成!赶紧把结果拿起走人吧ww
}

嗯嗯,你已经实现了老式的HP 计算器了!但是用户可不会RPN计算,你还需要一个中缀表达式转 RPN 的办法,我们需要写一个 parseExpression

因为我在写公式解析器,所以这里实际会遇到3种可能:中文变量,运算符,数字常量

运算符:栈中运算符弹出到输出队列,把这个运算符压入栈

数字:类型转换(String -> Number),放在输出队列。中文变量加给是否定义检查就好了

将所有东西全部弹到输出队列,就大功告成啦!

private static parseExpression(tokens: string[], context: Record<string, number>): number {
  const output: (number | string)[] = [];
  const operators: string[] = [];

  while (tokens.length > 0) {
    const token = tokens[0];
    if (this.isOperator(token)) {
      while (
        operators.length > 0
      ) {
        output.push(operators.pop()!);
      }
      operators.push(token);
      tokens.shift();
    } else if (!Number.isNaN(Number(token))) {
      output.push(this.parseNumber(token));
      tokens.shift();
    } else {
      if (!(token in context)) {
        throw new TypeError(`欸~ 变量“ ${token} ”没定义呢~ 连自己几斤几两都不清楚吗~杂鱼♡杂鱼♡`);
      }
      output.push(context[token]);
      tokens.shift();
    }
  }
  while (operators.length > 0) {
    output.push(operators.pop()!);
  }
  return this.evaluateRPN(output);
}

结束了?

还没完!比如后端发来了这种式子: =3 + 4 * 5

按照上面的方法,这将被解释:3 4 + 5 *,再翻译回来,却变成了 (3 + 4) * 5

没错!优先级!虽然纯 RPN 是不需要考虑优先级的,但是我们这里使用了 Shunting-yard 算法解析中缀表达式。我们对 + – 的优先级定为 1,* / 定义为 2,幂运算定义为3 …

然后在弹出栈时,加入判断比较优先级,来跳转输出顺序

while (
  operators.length > 0 &&
  this.getOperatorPrecedence(operators[operators.length - 1]) >= 
    this.getOperatorPrecedence(token)
) {
  output.push(operators.pop()!); // 弹出高优先级运算符
}
operators.push(token); // 当前运算符入栈

这总该结束了吧…

吗?

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇