calculator-by-str
Version:
Enter the string formula to get the calculation result.
280 lines (250 loc) • 7.6 kB
JavaScript
;
/**
* 此文件 用于生成测试公式
*/
/**
* 记录当前过程的日志 如果报错可以输出此列表
*/
let loglist = []
const myLog = (...arg) => {
}
/**
* 输出记录日志
* @param {...any} arg
*/
const myLog3 = (...args) => {
let list = args
loglist.push(list)
// console.log(...args)
}
/**
* 打印日志
* @param {...any} args
*/
const myLog2 = (...args) => {
console.log(...args)
}
const ErrorTree = () => {
console.log(loglist)
loglist = []
}
/**
* 加减方法
*/
const simple = {
["+"]: (a, b) => a + b,
["-"]: (a, b) => a - b
}
/**
* 乘除方法
*/
const simple2 = {
["*"]: (a, b) => a * b,
["/"]: (a, b) => a / b,
// ["%"]: (a, b) => a % b
}
/**
* 乘方先算
*/
const simple3 = {
["**"]: (a, b) => a ** b
}
const round = (a) => Math.floor(a + 0.5)
const mod = (a, b) => a % b
/**
* 支持的函数 和值
*/
const formulas = {
"Math.abs": [Math.abs, 1],
"Math.acos": [Math.acos, 1],
"Math.asin": [Math.asin, 1],
"Math.atan": [Math.atan, 1],
"Math.atan2": [Math.atan2, 2],
"Math.ceil": [Math.ceil, 1],
"Math.cos": [Math.cos, 1],
"Math.floor": [Math.floor, 1],
"Math.log": [Math.log, 1],
"Math.max": [Math.max, 2], // max 为了和其他语言同步 改成两个 如果需要用多个参数 可以把2改成"more"
"Math.min": [Math.min, 2], // min 为了和其他语言同步 改成两个 如果需要用多个参数 可以把2改成"more"
"Math.pow": [Math.pow, 2],
"Math.round": [round, 1],
"Math.sin": [Math.sin, 1],
"Math.sqrt": [Math.sqrt, 1],
"Math.tan": [Math.tan, 1],
"Math.mod": [mod, 2], // 非Math方法
"Math.PI": [Math.PI],
// "Math.SQRT2": [Math.SQRT2],
// "Math.LN10": [Math.LN10],
// "Math.LOG10E": [Math.LOG10E],
// "Math.LOG2E": [Math.LOG2E],
// "Math.SQRT1_2": [Math.SQRT1_2],
}
/**
* 函数类型
*/
let FmType = [
simple,
simple2,
formulas,
simple3,
]
/**
* 随机获取函数类型
* @returns simple or simple2 or formulas
*/
function getFmType() {
return Math.floor(Math.random() * FmType.length)
}
/**
* 根据tp类型 获取任意一种算法Math.* 或者加减乘除
* @param {*} tp
* @returns [function,key] or [number,key]
*/
function getRandomFmByType(tp = 0) {
let keys = Object.getOwnPropertyNames(FmType[tp])
let randomKey = keys[Math.floor(Math.random() * keys.length)]
myLog(randomKey, tp)
// 因为 Math.random = null 所有这里判断一下,不需要Math.random的原因是 测试Math.random不能确定结果是否正确
if (!FmType[tp][randomKey]) {
return getRandomFmByType(tp)
}
return [FmType[tp][randomKey], randomKey]
}
/**
* 获取随机数
* @param {*} num
* @returns -num <-> num
*/
function getRandomNum(num = 100) {
return Math.floor(Math.random() * num * 2) - num
}
/**
* 获取随机数
* @param {*} num
* @returns -num <-> num
*/
function getRandomNumAbs(num = 100) {
return Math.floor(Math.random() * num)
}
/**
* 生成一个公式和公式的计算结果
* @param {*} fmStr 被包含的公式,有概率不使用
* @param {*} num 值的使用取决于fmStr是否使用
* @returns [fmStr, value]
*/
function bornOne(fmStr = null, num = null) {
let type = getFmType()
let [curFmFunc, curFmStr] = getRandomFmByType(type)
let pcnt = 2
if (curFmFunc instanceof Array) {
[curFmFunc, pcnt] = curFmFunc
}
myLog3("bornOne ------------ ", { curFmFunc, curFmStr, fmStr })
if (curFmFunc instanceof Function) {
let funarg = []
let funStrArg = []
let ok = false
if (pcnt == "more") {
pcnt = getRandomNumAbs(10) + 2
}
let curFmRet = "("
for (let index = 0; index < pcnt; index++) {
let oneNum = getRandomNum()
let oneFm = oneNum
if (is() && fmStr && !ok) {
oneNum = num
oneFm = fmStr
ok = true
}
funarg.push(oneNum)
funStrArg.push(oneFm)
if (pcnt == index + 1) {
curFmRet = curFmRet + oneFm
} else {
curFmRet = curFmRet + oneFm + ","
}
}
curFmRet = curFmRet + ")"
let retNum = curFmFunc(...funarg)
if (type == 2) {
curFmRet = curFmStr + curFmRet
} else {
curFmRet = funStrArg[0] + curFmStr + funStrArg[1]
}
myLog3("...funarg", { pcnt, funarg, curFmRet, retNum, curFmStr, curFmFunc })
if (retNum == Infinity || retNum == -Infinity || isNaN(retNum)) {
myLog3("myLog error", retNum, fmStr, num)
return bornOne(fmStr, num)
}
return [curFmRet, retNum, ok]
} else {
return [curFmStr, curFmFunc]
}
}
/**
* 随机正反面
* @returns
*/
function is() {
return Math.floor(Math.random() * 2) == 0
}
/**
* 获取复杂组合公式
* @param {*} layer
* @returns
*/
function getRandomFm(layer) {
loglist = []
let curfmStr
let curNum
for (let index = 0; index < layer; index++) {
let [fmStr, fmNum, use] = bornOne(curfmStr, curNum)
if (use) {
curfmStr = "(" + fmStr + ")"
curNum = fmNum
if (curNum == Infinity || curNum == -Infinity || isNaN(curNum) || !curNum) {
return getRandomFm(layer)
}
} else {
let curs = simple
if (is()) {
curs = simple2
}
let keys = Object.getOwnPropertyNames(curs)
let key = keys[getRandomNumAbs(keys.length)] // 加减乘除公式
myLog("curfmStr, curNum", key, keys, curs)
let curFm // 右公式
let curNumr // 右值
if (curfmStr) { // 之前生成的公式 没有用到
curFm = curfmStr
curNumr = curNum
} else { // 第一次没有公式
let [cfmStr, cfmNum] = bornOne()
curFm = "(" + cfmStr + ")"
curNumr = cfmNum
}
// 加减乘除
curNum = curs[key](fmNum, curNumr)
// 生成函数的时候就防止NaN发生
if (curNum == Infinity || curNum == -Infinity || isNaN(curNum) || !curNum) {
return getRandomFm(layer)
}
// 新公式
// myLog3("每次结果1:", { index, curfmStr, curFm, curNumr, curNum })
curfmStr = "(" + "(" + fmStr + ")" + key + curFm + ")"
// myLog3("每次结果2:", { index, curfmStr, curFm, curNumr, curNum })
}
// myLog(curfmStr, curNum)
myLog3("每次结果:" + index, { fmStr, fmNum, use }, "上次结果:", { curfmStr })
}
myLog3("curfmStr", [curfmStr, curNum])
curfmStr = curfmStr.slice(1, curfmStr.length - 1) // 去掉两边括号
if (curNum == Infinity || curNum == -Infinity || isNaN(curNum) || !curNum) {
return getRandomFm(layer)
}
return [curfmStr, curNum]
}
// 还可以优化的点
// 1. 使用栈的方式解析括号
// 2. 优化如果0*(fmStr) 这个时候 fmStr 是可以不计算的
module.exports = { getRandomFm, ErrorTree };