本算法实现了计算算数表达式的运算,实现了加、减、乘、除四则运算,逻辑运算,三元运算等功能,类似javascript里的eval(expression)函数,完全可以用eval(expression)取代。
先看测试结果
!true||true ==> true
!false ==> true
!!false ==> false
!true ==> false
!!true ==> true
3>2&&3>4 ==> false
(1+1) ==> 2
(5-1)*2 ==> 8
2-(3-(1+4)) ==> 4
(1 + (2 - (3 * 4 / 5 + 6 - (7 + 8))) + (9 - 10) * 11 + 12) + 13 ==> 23.6
3<4&&5<4 ==> false
true||true ==> true
true||false ==> true
true&&!false ==> true
true||!false ==> true
true&&!(4<5) ==> false
!!true&&!!false ==> false
false?true:!false ==> true
false?4-3:4+3 ==> 7
on=='on'?'开启':'关闭' ==> 开启
可以使用二叉树拆分方式、堆栈方式等多种方式来实现该算法功能,之前文章介绍过堆栈方式实现,本文介绍二叉树方式实现该功能算法。本文实现了计算算数表达式的运算,实现了加、减、乘、除四则运算,逻辑运算,三元运算等功能。可以继续扩展函数的支持、参数变量的支持。有错误的请指教,下面直接上代码。
运算优先级,优先级相同从左到右
1 小括号 ( )
2 一元运算符 ++ -- !
3 算数运算符 * / %
4 算数运算符 + -
5 关系运算符 > >= < <=
6 相等运算符 == != === !==
7 逻辑运算符 &&
8 逻辑运算符 ||
9 三元运算符 :
10 三元运算符 ?
const operators = ['!','*','/','%','+','-','>','>=','<','<=','==','!=','===','!==','&&','||',':','?'];
const operleves = [2,3,3,3,4,4,5,5,5,5,6,6,6,6,7,8,9,10];
// 括号处理,先进后出,每一次出栈,即一对 ()
const stock = [];
const parenthesesPairPosition = {}
//是否是运算操作符
const isOperator = (oper) => {
return operators.indexOf(oper)>-1;
}
//获取优先级
const getOperatorLeve = (oper) => {
return operleves[operators.indexOf(oper)];
}
//判断是否是数字,校验只要是数字(包含正负整数,0以及正负浮点数)就返回true
const isNumber = (num) => {
let regPos = /^[-+]?\d+(\.\d+)?$/;
if(regPos.test(num)){
return true;
}else{
return false;
}
}
// 剔除两侧空格
const removeBlank = (expression, l, r) => {
while (expression[l] === ' ') {
l++
}
while (expression[r] === ' ') {
r--
}
return [l, r]
}
// 剔除两侧小括号
const removeParentheses = (l, r) => {
if (parenthesesPairPosition[l] === r) return [++l, --r]
return [l, r]
}
const findOperator = (v, v1, v2) => {
let op = '';
//'>=','<=','==','!=','===','!==','&&','||'
if(v==='&' || v==='=' || v==='|'){
if(v1==='>' || v1==='<' || v1==='=' || v1==='!' || v1==='&' || v1==='|'){
if(v2==='=' || v2==='!'){//'===','!=='
op = v2+v1+v;
let opleve = getOperatorLeve(op);
return [false, op, opleve, 3];
}else{
op = v1+v;
let opleve = getOperatorLeve(op);
return [false, op, opleve, 2];
}
}else{
if(isOperator(v)){
op = v;
let opleve = getOperatorLeve(op);
return [false, op, opleve, 2];
}
}
}else if(isOperator(v)){
op = v;
let opleve = getOperatorLeve(op);
return [false, op, opleve, 1];
}
return [true, op, 0, 0];
}
按照运算优先级拆分字符串表达式,装换成二叉树算法
//将表达式转换成二叉树结构
const parse = (expression, l = 0, r = expression.length - 1, skipSearchTime = false) => {
let isNum = true;
let parenthesesDep = 0; // 记录小括号深度
let firstTimeOperator = null; // 记录遇到的第一个 * / 运算符
let firstTimeOperatorIdx = null; // 记录遇到的第一个 * / 运算符的位置
let firstTimeOperatorLeve = -1;
let firstTimeOperatorlength = 1;
[l, r] = removeBlank(expression, l, r);
[l, r] = removeParentheses(l, r);
for (let i = r; i >= l; i--) {
const v = expression[i];
if(v===' '){
continue;
}
if (v === ')') {
stock.push(i);
parenthesesDep++;
continue;
} else if (v === '(') {
const lnode = stock.pop();
parenthesesPairPosition[i] = lnode;
parenthesesDep--;
isNum = false;
}
//遍历到最左边
if (i === l && v!=='!') {
if (isNum && firstTimeOperator==null) {
let val = isNumber(expression.slice(l, r + 1).trim())?Number(expression.slice(l, r + 1).trim()):expression.slice(l, r + 1).trim();
return {
type: 'number',
value: val,
};
}
if (parenthesesPairPosition[i] === r) {//去掉前后括号后进行递归表达式
return parse(expression, l + 1, r - 1);
}
// 高优先级 拆分,需要遍历到最左侧,所以拆分逻辑写这里
return {
type: firstTimeOperator,
left: parse(expression, l, firstTimeOperatorIdx - 1, true),
right: parse(expression, firstTimeOperatorIdx + firstTimeOperatorlength, r),
}
}
//找到优先级最高的运算符
let oplen = 0;
let opleve = 0;
let op = '';
let v1, v2;
if(i>l){
v1 = expression[i-1];
}
if(i>(l+1)){
v2 = expression[i-2];
}
[isNum, op, opleve, oplen] = findOperator(v, v1, v2);
if(isNum){
continue;
}else{
if(oplen==2){
i--;
}else if(oplen==3){
i = i-2;
}
}
if(opleve>firstTimeOperatorLeve && parenthesesDep==0){
firstTimeOperator = op;
firstTimeOperatorIdx = i;
firstTimeOperatorLeve = opleve;
firstTimeOperatorlength = oplen;
}
//单目运算符!的支持
if(i === l && v==='!'){
if(firstTimeOperator==='!'){
return {
type: firstTimeOperator,
right: parse(expression, l + 1, r),
}
}else{
return {
type: firstTimeOperator,
left: parse(expression, l, firstTimeOperatorIdx - 1, true),
right: parse(expression, firstTimeOperatorIdx + firstTimeOperatorlength, r),
}
}
}
}
}
计算按照运算优先级转换而来的二叉树的结果
//计算二叉树表达式结果
const exec = node => {
const recursion = node => {
if (node.type === 'number') {
let value = node.value;
if(typeof value === 'string'){
return value.replaceAll('\'', '')
}
return value;
}else{
if (node.type === '!') {
let val = recursion(node.right);
if(typeof val === 'boolean'){
return !val;
}else{
return val=='false';
}
} else if (node.type === '*') {
return recursion(node.left) * recursion(node.right);
} else if (node.type === '/') {
return recursion(node.left) / recursion(node.right);
} else if (node.type === '%') {
return recursion(node.left) % recursion(node.right);
} else if (node.type === '+') {
return recursion(node.left) + recursion(node.right);
} else if (node.type === '-') {
return recursion(node.left) - recursion(node.right);
} else if (node.type === '>') {
return recursion(node.left) > recursion(node.right);
} else if (node.type === '<') {
return recursion(node.left) < recursion(node.right);
}else if (node.type === '<=') {
return recursion(node.left) <= recursion(node.right);
}else if (node.type === '>=') {
return recursion(node.left) >= recursion(node.right);
}else if (node.type === '==') {
return recursion(node.left) == recursion(node.right);
}else if (node.type === '!=') {
return recursion(node.left) != recursion(node.right);
}else if (node.type === '===') {
return recursion(node.left) === recursion(node.right);
}else if (node.type === '!==') {
return recursion(node.left) !== recursion(node.right);
}else if (node.type === '&&') {
return recursion(node.left) && recursion(node.right);
}else if (node.type === '||') {
return recursion(node.left) || recursion(node.right);
}else if (node.type === ':') {
return recursion(node.left) +':'+ recursion(node.right);
}else if (node.type === '?') {
let val = recursion(node.left);
let values = recursion(node.right);
if(typeof val === 'boolean'){
if(val){
values = values.slice(0, values.indexOf(':')-values.length);
}else{
values = values.slice(values.indexOf(':')+1);
}
}else{
if(val=='false'){
values = values.slice(values.indexOf(':')+1);
}else{
values = values.slice(0, values.indexOf(":")-values.length);
}
}
return values;
}
}
}
return recursion(node)
}
测试用例
//测试用例
let expression = '!true||true';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '!!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '!true';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '!!true';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '3>2&&3>4';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '(1+1)';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '(5-1)*2';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '2-(3-(1+4))';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '(1 + (2 - (3 * 4 / 5 + 6 - (7 + 8))) + (9 - 10) * 11 + 12) + 13';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '3<4&&5<4';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'true||true';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'true||false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'true&&!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'true||!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'true&&!(4<5)';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = '!!true&&!!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'false?true:!false';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = 'false?4-3:4+3';
console.log(expression, '==>', exec(parse(expression)), ' <--对比--> ', eval(expression));
expression = "on=='on'?'开启':'关闭'";
console.log(expression, '==>', exec(parse(expression)));
测试结果
!true||true ==> true <--对比--> true
!false ==> true <--对比--> true
!!false ==> false <--对比--> false
!true ==> false <--对比--> false
!!true ==> true <--对比--> true
3>2&&3>4 ==> false <--对比--> false
(1+1) ==> 2 <--对比--> 2
(5-1)*2 ==> 8 <--对比--> 8
2-(3-(1+4)) ==> 4 <--对比--> 4
(1 + (2 - (3 * 4 / 5 + 6 - (7 + 8))) + (9 - 10) * 11 + 12) + 13 ==> 23.6 <--对比--> 23.6
3<4&&5<4 ==> false <--对比--> false
true||true ==> true <--对比--> true
true||false ==> true <--对比--> true
true&&!false ==> true <--对比--> true
true||!false ==> true <--对比--> true
true&&!(4<5) ==> false <--对比--> false
!!true&&!!false ==> false <--对比--> false
false?true:!false ==> true <--对比--> true
false?4-3:4+3 ==> 7 <--对比--> 7
on=='on'?'开启':'关闭' ==> 开启