栈计算器
加减乘除 maths.js
function add(a, b) {
return a + b;
}
function reduce(a, b) {
return a - b;
}
function rede(a, b) {
return a * b;
}
function except(a, b) {
return a / b;
}
字符匹配 tools.js
function trim(str, isGlobal = false) {
var result;
result = str.replace(/(^\s+)|(\s+$)/g, "");
if (isGlobal) {
result = result.replace(/\s/g, "");
}
return result;
}
function dbBrackets(str) {
const strArr = str.split("");
const breacketNum = strArr.reduce((acc, next) => {
if (next === "(") {
acc += 1;
}
if (next === ")") {
acc -= 1;
}
if (acc === -1) {
acc = NaN;
}
return acc;
}, 0);
return breacketNum === 0 ? str : false;
}
function isNum(num) {
return typeof num === "number" && isFinite(num);
}
function isNumStr(str) {
return str.search(/^([1-9]\d*|0)(\.\d+)?$/) !== -1;
}
栈 Stack.js
class Stack {
constructor() {
this.store = [];
}
top() {
const item = this.out();
this.in(item);
return item;
}
reverse(save = false) {
let cache = [...this.store].reverse();
if (save) {
this.store = cache;
}
return cache;
}
in(data) {
this.store.push(data);
}
out() {
return this.store.pop();
}
reOut() {
return this.store.shift();
}
isNull() {
return !this.store.length;
}
len() {
return this.store.length;
}
}
export default { Stack };
计算器 counter.js
import { Stack } from "./Stack";
import { add, reduce, rede, except } from "./maths";
import { trim, dbBrackets, isNum, isNumStr } from "./tools";
class Counter {
constructor(errFn) {
this.math = new Map([]);
this.stack = new Stack();
this.valids = [dbBrackets, ...this.validDef()];
this.errFn = errFn;
this.setDefMath();
}
toError() {
this.errFn && this.errFn();
}
setDefMath() {
this.register("+", add, 1);
this.register("-", reduce, 1);
this.register("*", rede, 2);
this.register("/", except, 2);
}
addValid(valids) {
if (Array.isArray(valids)) {
this.valids = [...this.valids, ...valids];
} else {
this.valids.push(valids);
}
}
valid(data) {
return this.valids.reduce((acc, next) => {
return acc === false ? false : next(acc);
}, data);
}
register(symbol, fn, weight) {
if (typeof fn !== "function") {
weight = fn;
fn = null;
}
this.math.set(symbol, {
fn,
weight,
});
}
validDef() {
const _this = this;
function validSymbol(str) {
const rxSymbol = _this.getRx(_this.getSymbol());
const after = str.replace(rxSymbol, "");
return !!!after ? str : false;
}
function prefix(str) {
const rxNum = /^\d|^\(/gim;
return str.search(rxNum) !== -1 ? str : false;
}
return [validSymbol, prefix];
}
getSymbol() {
return [...this.math.keys(), "(", ")"];
}
getRx(symbol) {
let rule = symbol.reduce((acc, next) => {
return `${acc}${acc === "" ? "" : "|"}\\${next}`;
}, "");
rule = `${rule}|([1-9]\\d*|0)(\\.\\d+)?`;
return new RegExp(rule, "igm");
}
getResult(stack) {
let numStack = new Stack();
let math = this.math;
stack = stack || this.stack;
while (!stack.isNull()) {
let top = stack.reOut();
if (isNum(top)) {
numStack.in(top);
} else {
let currentMath = math.get(top).fn;
let secentNum = numStack.out();
let firstNum = numStack.out();
numStack.in(currentMath(firstNum, secentNum));
}
}
return numStack.out();
}
resolver(formula) {
formula = trim(formula);
if (!this.valid(formula)) {
return this.toError();
}
const math = this.math;
const symbol = this.getSymbol();
const rx = this.getRx(symbol);
const codes = formula.match(rx);
const symbols = new Stack();
const stack = this.stack;
codes.forEach((code) => {
if (isNumStr(code)) {
stack.in(parseFloat(code));
} else {
if (symbols.isNull() || code === "(" || symbols.top() === "(") {
symbols.in(code);
} else if (code == ")") {
let top = symbols.out();
while (top !== "(") {
stack.in(top);
top = symbols.out();
}
} else {
let top = symbols.out();
let topWeight = math.get(top).weight;
let codeWeight = math.get(code).weight;
while (
top !== "(" &&
!symbols.isNull() &&
topWeight >= codeWeight
) {
stack.in(top);
top = symbols.out();
if (top !== "(") {
topWeight = math.get(top).weight;
}
}
if (topWeight >= codeWeight) {
stack.in(top);
} else {
symbols.in(top);
}
symbols.in(code);
}
}
});
while (!symbols.isNull()) {
stack.in(symbols.out());
}
return stack;
}
}
export default { Counter };