活跃农民
- 积分
- 511
- 大米
- 颗
- 鳄梨
- 个
- 水井
- 尺
- 蓝莓
- 颗
- 萝卜
- 根
- 小米
- 粒
- 学分
- 个
- 注册时间
- 2013-6-5
- 最后登录
- 1970-1-1
|
0224 1. Basic Calculator IV
/**
* @param {string} expression
* @param {string[]} evalvars
* @param {number[]} evalints
* @return {string[]}
*/
var basicCalculatorIV = function(expression, evalvars, evalints) {
if (expression == null) {return ''}
evalvars = evalvars || []
evalints = evalints || []
// !!! you cannot just directly replace with string, because there might be names containing the replace name
const map = new Map()
for (let i = 0; i < evalvars.length; i++) {
map.set(evalvars[i], evalints[i]);
}
// !!! note they didn't separat parentheses out
expression = expression.replace(/\(/g, ' ( ')
expression = expression.replace(/\)/g, ' ) ')
expression = expression.split(' ').filter(str => str).map(str => {
return map.has(str) ? map.get(str) : str
}).join(' ')
return toList(shunt(expression))
};
function calc (e1, e2, oper) {
const map = new Map()
switch (oper) {
case '*':
// !!! otherwise the iterator will be exhausted in the double for loop
const keys1 = [...e1.keys()]
const keys2 = [...e2.keys()]
for (let key1 of keys1) {
const co1 = e1.get(key1)
for (let key2 of keys2) {
const co2 = e2.get(key2)
const co = calcTwo(co1, co2, oper)
const strs1 = key1.split('*')
const strs2 = key2.split('*')
// !!!! filter step is important, otherwise you'll have useless *s
const key = [...strs1, ...strs2].filter(str => str).sort().join('*')
const val = map.get(key) || 0
map.set(key, val + co)
}
}
break; // !!! automatic fall through !!! shit
default:
const keys = new Set([...e1.keys(), ...e2.keys()])
for (let key of keys) {
const a = e1.get(key) || 0
const b = e2.get(key) || 0
const res = calcTwo(a, b, oper)
map.set(key, res)
}
break;
}
return map
}
function calcTwo (num1, num2, oper) {
switch (oper) {
case '+': return num1 + num2
case '-': return num1 - num2
case '*': return num1 * num2
default: throw new Error('No such type')
}
}
function toList (eMap) {
const keys = [...eMap.keys()].sort((key1, key2) => {
const strs1 = key1.split('*')
const strs2 = key2.split('*')
// !!! '' and a, both don't have the *, will have same length
// so cope with '' first
// !!! and note reverse order
if (!key1) {return 1}
if (!key2) {return -1}
if (strs1.length !== strs2.length) {
return strs2.length - strs1.length
} else {
if (key1 > key2) {
return 1
} else if (key1 < key2) {
return -1
} else {
return 0
}
}
})
// !!! put the filter here because it could never enter the calc function, like: '0'
return keys.filter(key => eMap.get(key)).map(key => {
return key
? `${eMap.get(key)}*${key}`
: `${eMap.get(key)}`
})
}
// +, -, *, (, ), 45, abcde
function shunt (str) {
const tokens = str.split(' ')
const output = []
const stack = []
const calcNext = (oper) => {
const b = output.pop()
const a = output.pop()
const res = calc(a, b, oper)
output.push(res)
}
const compare = (a, b) => {
const dict = {
'+': 1,
'-': 1,
'*': 2,
'(': -1
}
return dict[a] - dict[b]
}
for (let token of tokens) {
if (isNumber(token)) {
output.push(new Map([['', Number.parseInt(token, 10)]]))
} else if (token === '(') {
stack.push('(')
} else if (token === ')') {
while (true) {
const aToken = stack.pop()
if (aToken === '(') {
break;
}
calcNext(aToken)
}
} else if (['+', '-', '*'].includes(token)) {
while (stack.length > 0 && compare(token, stack[stack.length - 1]) <= 0) { // todo: check this // !!! and test for empty
const aToken = stack.pop()
calcNext(aToken)
}
stack.push(token)
} else { // var
output.push(new Map([[token, 1]]))
}
}
while (stack.length > 0) {
calcNext(stack.pop())
}
return output[0]
}
function isNumber (str) {
const val = Number.parseInt(str, 10)
return Number.isInteger(val)
}
|
|