ysyx:对数学表达式分析的理解
要解析一个带有括号的长表达式并没有想象中那么容易。
首先,数学表达式的递归分解顺序和日常的顺序是完全相反的。在标准的数学四则表达式里,我们的计算顺序是从左往右,从高到低,优先计算括号内容。 由于我们使用的是递归的思路,代码拆分表达式的过程和计算顺序是完全相反的: 一方面,原本的高优先级级运算符要后进行拆分,先拆低优先级运算符。另一方面,原本的从左到右的运算顺序使得拆分运算符时要从最右侧进行拆分。
我一开始对括号处理的理解有问题,识别括号时,不是两侧有括号+括号匹配就代表正确,这种理解导致的括号匹配函数写了很多没必要的内容。
按照讲义提供的逻辑,最外层括号要能包住完整式子,这才是有效的外层括号.
比如这样的一个表达式:((839*(928/((((972)))))-505)-((((666)))-(746*796/723*408-((713))))+365)
第一步肯定是去掉外层括号,然后按照+把前半部分和365切开。但是,下一步就有问题了:
(839*(928/((((972)))))-505) - ((((666)))-(746*796/723*408-((713)))) 这一部分,看起来两侧有括号,而且括号左右匹配,那应该继续返回true,去掉最外层再继续吗?实际上不行,因为最外层的括号并不是包裹整个式子的。
如果去掉多余括号,变成(839*(928/972)-505) - (666-(746*796/723*408-713))。可以看到这个式子其实已经没有最外层括号了,所以应该返回的是false,按照中间的减号拆分。要做到这一点,可以考虑加减左右括号,如果还没到末尾左右括号就对应消耗完了,说明最外层括号已经没了。如果括号不匹配,那直接报错暂停就行了,没没必要再传false给eval函数。
一生一芯的讲义其实已经提供了良好的思路,按照讲义的思路走就可以了。唯一需要单独处理的就是解地址符号,因为这个是单目运算符。我在考虑之后,增加了一个函数,作用是在扫描出指针和地址token以后,去掉指针token,因为已经没必要留着了,留着反而会影响表达式的递归分割,数值直接存在地址token的附加部分就行。此外,如果有负数,也可以考虑用类似的思路去单独处理。自增和自减比较麻烦,可能需要进一步增加token类型。
----------------------------------------------------
第二个遇到的问题是:代码里全程都使用的是uint32,但这样存在一个问题,就是在计算除法时,假如被除数和除数里有一个是负数(按人的理解),在uint里会被强制转换为正数,导致计算出问题,所以如果想全程保持uint,那么除法这里就需要把两个数先强转int型再计算。
举例:(((261))-826*(739)*(480-((789+863)))/(542)/((991))) 在计算到-715405208 /542 这一步时就出现了问题。正确结果是-1319935,但是计算成了6604358。原本的被除数被理解成了一个超大的uint,导致计算结果是一个正数(int环境)。这显然有问题。为了保险起见,我又测试了一下乘法,似乎不存在这个问题。在DIV的case里强转int后,问题解决了。