I am working on a calculator project using an array. I wanted to allow the user to write multiple functions before finding the answer, similar to a Casio fx-300ES Plus. Right now I am working on multiplication before moving to other operators. To do this I thought the best way is to find the index at which all 'x' are located using a for-loop, then run two other for-loops, one looking at the left of the operator, the other looking at the right of it. Once it finds another operator, it will break. I could then store the information next to the 'x' using slice().
The problem I have run in to is when the numbers between operators is 1. If I use slice() it does not work as there is no information between the indexes. Is there another way to store these numbers into an array?
Any information on this is much appreciated.
var array = ['7', '3', ' ', '6', 'x', '8', ' ', '5', '4', 'x', '2'];
//for loop checking for 'x' symbols
for (var i = 0; i < array.length; i ){
console.log("i " array[i]);
//if there is an 'x'
if (array[i] == 'x') {
console.log('index is at ' i);
//create an array to eventually store the values
var newArray = new Array();
//checks for the index where j is NaN on the LEFT side
for (j = i - 1; j > 0; --j){
if (isNaN(array[j])){
console.log('j is ' j);
break;
}
}
//checks for the index where e is NaN on the RIGHT side
for (e = i 1; e < array.length; e )
{
if (isNaN(array[e])){
console.log('e is at ' e);
break;
} else if (e == array.length - 1) {
console.log('e is at array length of ' e);
break;
}
}
//add the numbers between j and i to newArray
newArray = array.slice(j 1, i);
console.log(newArray);
//add the numbers between i and e to newArray
newArray = array.slice(i 1, e);
console.log(newArray);
console.log("array of slice is " newArray);
}
}
CodePudding user response:
You could write tokenize
which consumes your text input and produces a stream of tokens -
const input =
[ '7', '3', ' ', '6', 'x', '8', ' ', '5', '4', 'x', '2' ]
function* tokenize (es) {
let r = 0, n
for (const e of es) {
switch (e) {
case " ":
case "x":
yield { number: r }
yield { operation: e }
r = 0
break
default:
n = Number.parseInt(e, 10)
if (Number.isNaN(n))
throw Error(`unexpected input: ${e}`)
else
r = r * 10 n
break
}
}
if (r > 0) yield { number: r }
}
for (const t of tokenize(input))
console.log(t)
{"number":73}
{"operation":" "}
{"number":6}
{"operation":"x"}
{"number":8}
{"operation":" "}
{"number":54}
{"operation":"x"}
{"number":2}
Followed by parse
which consumes a stream of tokens and produces a abstract syntax tree -
function parse (ts) {
let s = e => e
for (const t of ts) {
if (t?.number) {
let r = s(t)
s = _ => r
}
else if (t?.operation) {
let left = s()
s = right => ({ operation: t.operation, left, right })
}
else {
throw Error(`unexpected token: ${JSON.stringify(t)}`)
}
}
return s()
}
console.log(parse(tokenize(input)))
{
operation: "x",
left: {
operation: " ",
left: {
operation: "x",
left: {
operation: " ",
left: { number: 73 },
right: { number: 6 }
},
right: { number: 8 }
},
right: { number: 54 }
},
right: { number: 2 }
}
Finally eval
which evaluates the syntax tree as a program -
function eval (e) {
if (e?.number)
return e.number
else if (e?.operation)
return evalOp(e)
else
throw Error(`unexpected expression: ${JSON.stringify(e)}`)
}
We simplify eval
by writing evalOp
separately. This technique is known as mutual recursion and is super effective for traversing tree-like structures -
function evalOp (e) {
switch (e?.operation) {
case " ": return eval(e.left) eval(e.right)
case "x": return eval(e.left) * eval(e.right)
default: throw Error(`unexpected operation: ${e.operation}`)
}
}
Bring tokenize
, parse
, and eval
together to compute the result -
const input =
[ '7', '3', ' ', '6', 'x', '8', ' ', '5', '4', 'x', '2' ]
console.log(eval(parse(tokenize(input))))
1372
This is correct when evaluated using pocket calculator order of operations -
73
... 6 = 79
... * 8 = 632
... 54 = 686
... * 2 = 1372
If you wanted to evaluate using a different order of operations, ie PEMDAS, you would have to rewrite parse
to interpret the stream of tokens as a different program.
Expand the snippet below to verify the result in your browser -