小学生都能看懂的表达式计算(图解)

基础知识

 

会加减乘除(纳尼,这个还有不会的吗

会双向链表和树(纳尼,还个还要会吗

 

温故知新

 

表达式 2 + 3 - 4 + 5,人类计算的过程是这样的,如下图:

因为加减操作符优先级相同,所以从左到右依次运算。

 

可计算机不会这样啊,必须先转化为一棵树(AST,抽象语法树,这是编译原理中的概念)才行,如下图:

操作符上提变为根节点,左右操作数下降变为叶子节点,它们整体又成了一个操作数。依次对每个操作符使用这个规则,表达式即可变成一棵树。

 

再接再厉

 

表达式 2 + 3 × 4 - 5 ÷ 6,人类的计算过程是这样的,如下图:

按照优先级从高到低,先乘除,再加减。

 

要想让计算机来算啊,还得先转换为一棵AST才行,如下图:

 

发现问题

 

综上,不难发现,先要把一个表达式转化为一棵AST。人类根据操作符的优先级很自然地把一个表达式转化为一棵树,但是计算机该怎么做呢?

 

模仿人类

 

人类的视线可以在表达式上随意扫描,对于不复杂的,一眼就发现优先级高的操作符,瞬间拿到它两边的操作数,就可以进行转化了。

 

计算机在方式上无法和人类相比,但是在结果上必须要相同,即也要找到优先级高的操作符,也要知道它两边的操作数,然后再进行转化。

 

回归程序

 

定义一下操作符的优先级,优先级的数值没有要求,如下图:

 

当我们拿到一个操作符时,也要能拿到它左右两边的操作数,因操作符和操作数是间隔互联的,实际它们就是一个双向链表。

 

因我们要找出优先级高的操作符,所以必须先找出所有的操作符,然后再判断优先级的高低。

 

把整个表达式里的操作数和操作符用双向链表串起来,并把所有的操作符放入一个列表里。如下图:

 

按优先级从高到低对操作符列表排序,优先级高的前移,优先级低的后移,优先级相同的相对位置不变。如下图:

 

取出排在第一位的×,去双向链表里找出它的左右操作数3和4,把这三个节点转化为一棵树,再把该树作为操作数替换掉原来的三个节点,同时修复好与前面 + 号和后面 - 号的双向链接。如下图:

 

取出第二位的÷,采用和上面相同的方式进行转化。如下图:

 

取出第三位的+,采用相同的方式转化。如下图:

 

取出最后一位的-,采用相同的方式转化后,整个表达式转化完毕,变为一棵AST。如下图:

 

推而广之

 

操作符有了优先级之后,就限制了表达式的计算顺序,人们有时希望打破这种限制,就引入了更神奇的操作符,就是小括号啦(哈哈)。

 

表达式 ( 2 + 3 ) × ( ( 4 - 5 ) ÷ 6 ),如果还想采用上面的套路,那么核心问题就落到了操作符的优先级上了。即如何真实地反映每个操作符的优先级值。

 

此时我们意识到,操作符的优先级不再是固定的,而是会随着有没有括号以及括号的嵌套深度而变化。其实小括号本来就是改变了操作符的优先级嘛,我们人类明白这一点,关键也要让计算机明白。

 

既然括号是操作符,那也为它定义一个优先级值,这个值最好稍微大一些。如下图:

 

定义一个上下文的基础优先级值,默认当然是0了。

 

当遇到左括号时,基础优先级值加上括号优先级值,相当于基础优先级值得到了提升,这样括号里的操作符因为有基础优先级值的存在,所以自然抬高了自己的优先级值(相等于站到了巨人的肩膀上)。

 

当遇到右括号时,基础优先级值减去括号优先级值,相当于此时新的基础优先级值降到了进入括号前的水准。如下图:

 

解析表达式,同时计算出每个操作符的优先级值(牢记,遇到一个左括号加100,遇到一个右括号减100)。如下图:

 

按照实际优先级值从高到低排序操作符列表。如下图:

 

最后按照相同的方式,转换为一棵AST。如下图:

 

后记:人类社会存在的历史要比计算机的历史长的多,从人类社会寻找问题的解决思路有时也是一个不错的方向。甚至可以从大自然中寻找。

 

 

(完)

 

编程新说


用独特的视角说技术

Python 正则表达式是一种强大的文本处理工具,它允许开发者通过模式匹配来搜索、替换和分割字符串。虽然不是图形化的,但你可以理解它们就像一个描绘字符组合的语言。正则表达式通常包含以下几个元素: 1. **字符集** (`[]`):匹配括号内的任何一个字符。例如,`[abc]` 匹配 'a', 'b', 或 'c'。 2. **元字符**: - `.`:匹配除了换行符以外的任意单个字符。 - `^`:匹配输入字符串的开始。 - `$`:匹配输入字符串的结束。 - `*`:表示前面的字符可以出现0次或多次。 - `+`:表示前面的字符至少出现一次。 - `?`:表示前面的字符出现0次或1次。 - `{n}`:指定前面的字符恰好出现 n 次。 - `{n, m}`:指定前面的字符至少出现 n 次,至多出现 m 次。 3. **分组** (`()`):将部分模式作为整体处理,并可以用编号引用或回溯。 4. **特殊序列**: - `\d`: 匹配数字。 - `\D`: 匹配非数字字符。 - `\w`: 匹配字母、数字和下划线。 - `\W`: 匹配非字母、数字和下划线的字符。 - `\s`: 匹配空白字符(空格、制表符等)。 - `\S`: 匹配非空白字符。 5. **边界匹配**: - `\b`: 匹配单词边界。 - `\B`: 匹配非单词边界。 6. **预定义字符类**:如`\A`(匹配输入字符串开始)、`\Z`(匹配输入字符串结束,如果它是整个输入,而不是只匹配到行尾)。 学习正则表达式图解通常是看在线教程或参考文档,比如regex101.com,那里有可视化的实时匹配示例。不过,Python标准库re模块提供了函数,如`search()`, `match()`, `findall()`等,直接用于编写和测试正则表达式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值