Is There An Alternative To Slice In Javascript?
Solution 1:
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 = 0breakdefault:
n = Number.parseInt(e, 10)
if (Number.isNaN(n))
throwError(`unexpected input: ${e}`)
else
r = r * 10 + n
break
}
}
if (r > 0) yield { number: r }
}
for (const t oftokenize(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 -
functionparse (ts) {
lets = e => e
for (const t of ts) {
if (t?.number) {
let r = s(t)
s = _ => r
}
elseif (t?.operation) {
let left = s()
s = right => ({ operation: t.operation, left, right })
}
else {
throwError(`unexpected token: ${JSON.stringify(t)}`)
}
}
returns()
}
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 -
functioneval (e) {
if (e?.number)
return e.numberelseif (e?.operation)
return evalOp(e)
elsethrowError(`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 -
functionevalOp (e) {
switch (e?.operation) {
case"+": returneval(e.left) + eval(e.right)
case"x": returneval(e.left) * eval(e.right)
default: throwError(`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 -
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 = 0breakdefault:
n = Number.parseInt(e, 10)
if (Number.isNaN(n))
throwError(`unexpected input: ${e}`)
else
r = r * 10 + n
break
}
}
if (r > 0) yield { number: r }
}
functionparse (ts) {
lets = e => e
for (const t of ts) {
if (t?.number) {
let r = s(t)
s = _ => r
}
elseif (t?.operation) {
let left = s()
s = right => ({ operation: t.operation, left, right })
}
else {
throwError(`unexpected token: ${JSON.stringify(t)}`)
}
}
returns()
}
functioneval (e) {
if (e?.number)
return e.numberelseif (e?.operation)
return evalOp(e)
elsethrowError(`unexpected expression: ${JSON.stringify(e)}`)
}
functionevalOp (e) {
switch (e?.operation) {
case"+": returneval(e.left) + eval(e.right)
case"x": returneval(e.left) * eval(e.right)
default: throwError(`unexpected operation: ${e.operation}`)
}
}
console.log(eval(parse(tokenize(input))))
Post a Comment for "Is There An Alternative To Slice In Javascript?"