Skip to content Skip to sidebar Skip to footer

Is There An Alternative To Slice In Javascript?

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

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?"