ny
22 小时以前 282fbc6488f4e8ceb5fda759f963ee88fbf7b999
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
 * 将用户输入的连续单个数字合并为一个数
 * @param {Array} expressions - 记录计算表达式的数组
 * @returns {Array} 新的数组
 */
export const mergeNumberOfExps = (expressions) => {
  const res: any = [];
  const isNumChar = (n) => /^[\d|.]$/.test(n);
  for (let i = 0; i < expressions.length; i++) {
    if (i > 0 && isNumChar(expressions[i - 1]) && isNumChar(expressions[i])) {
      res[res.length - 1] += expressions[i];
      continue;
    }
    res.push(expressions[i]);
  }
  return res;
};
/**
 * 校验表达式是否符合计算法则
 * @param {Array} expressions - 合并数字后的表达式数组
 * @returns {boolean}
 */
export const validExp = (expressions, mergeNum = true) => {
  const temp = mergeNum ? mergeNumberOfExps(expressions) : expressions;
  const arr = temp.filter((t) => !'()'.includes(t));
  // 去括号后 length应该为奇数  并且第一个字符和最后一个字符应该为数字而非计算符号
  if (
    temp.length % 2 === 0 ||
    arr.length % 2 === 0 ||
    Number.isNaN(+arr[0]) ||
    Number.isNaN(+arr[arr.length - 1])
  ) {
    return false;
  }
  for (let i = 0; i < arr.length - 1; i += 2) {
    if (typeof +arr[i] !== 'number' || !Number.isNaN(+arr[i + 1])) return false;
  }
  return true;
};
export const validDateExp = (expressions, mergeNum = true) => {
  const temp = mergeNum ? mergeNumberOfExps(expressions) : expressions;
  // length应该3的倍数、 以+/-号开头、以y/m/d/h/m/s结尾、中间应该为数字
  if (temp.length % 3 !== 0) {
    return false;
  }
  for (let i = 0; i < temp.length - 1; i += 3) {
    if (/^\+\d+|-\d+$/.test(temp[i]) || Number.isNaN(+temp[i + 1]))
      return false;
  }
  return true;
};
/**
 * 校验日期计算表达式是否符合计算法则
 * @param {Array} expressions - 合并数字后的表达式数组
 * @returns {boolean}
 */
/**
 * 中缀转后缀(逆波兰 Reverse Polish Notation)
 * @param {Array} exps - 中缀表达式数组
 */
export const toRPN = (exps) => {
  const s1: any[] = []; // 符号栈
  const s2: any[] = []; // 输出栈
  const getTopVal = (stack) =>
    stack.length > 0 ? stack[stack.length - 1] : null;
  const levelCompare = (c1, c2) => {
    const getIndex = (c) => ['+-', '×÷', '()'].findIndex((t) => t.includes(c));
    return getIndex(c1) - getIndex(c2);
  };
  exps.forEach((t) => {
    if (typeof t === 'string' && Number.isNaN(Number(t))) {
      // 是符号
      if (t === '(') {
        s1.push(t);
      } else if (t === ')') {
        let popVal;
        do {
          popVal = s1.pop();
          popVal !== '(' && s2.push(popVal);
        } while (s1.length && popVal !== '(');
      } else {
        let topVal = getTopVal(s1);
        if (topVal) {
          while (topVal && topVal !== '(' && levelCompare(topVal, t) >= 0) {
            // 优先级 >= t 弹出到s2
            s2.push(s1.pop());
            topVal = getTopVal(s1);
          }
          s1.push(t);
        } else {
          // s1 为空 直接push
          s1.push(t);
        }
      }
      return;
    }
    s2.push(t); // 数字直接入栈
  });
  while (s1.length) {
    s2.push(s1.pop());
  }
  return s2;
};
/**
 * 计算后缀表达式的值
 * @param {Array} rpnExps - 后缀表达式
 */
export const calcRPN = (rpnExps) => {
  rpnExps = [...rpnExps];
  const calc = (x, y, type) => {
    const a1 = Number(x);
    const a2 = Number(y);
    switch (type) {
      case '+': {
        return a1 + a2;
      }
      case '-': {
        return a1 - a2;
      }
      case '÷': {
        return a1 / a2;
      }
      case '×': {
        return a1 * a2;
      }
    }
  };
  for (let i = 2; i < rpnExps.length; i++) {
    if ('+-×÷'.includes(rpnExps[i])) {
      const val = calc(rpnExps[i - 2], rpnExps[i - 1], rpnExps[i]);
      rpnExps.splice(i - 2, 3, val);
      i = i - 2;
    }
  }
  return rpnExps[0];
};