1

This code works as a calculator, but the scratch pad at codeacademy tells me that eval is evil. Is there another way to do the same thing without using eval?

var calculate = prompt("Enter problem");
alert(eval(calculate));
10
  • 2
    You want to parse an equation from a string into working code? Then you'll need to build a parser. Commented Feb 17, 2013 at 0:57
  • 1
    And building a parser is no easy task. Commented Feb 17, 2013 at 0:59
  • 2
    @AdamRackis well that's true but something to handle infix arithmetic isn't that bad - it's probably the best "my first parser" project :-) Commented Feb 17, 2013 at 1:00
  • 1
    @VisioN: That's effectively eval. Commented Feb 17, 2013 at 1:03
  • 3
    @VisioN that's like replacing cheddar with mozzarella for a lactose intolerant person :-) Commented Feb 17, 2013 at 1:03

4 Answers 4

5

eval evaluates the string input as JavaScript and coincidentally JavaScript supports calculations and understands 1+1, which makes it suitable as a calculator.

If you don't want to use eval, which is good, you have to parse that string yourself and, finally, do the computation yourself (not exactly yourself though). Have a look at this math processor, which does what you want.

Basically what you do is:

  1. Read the input string char by char (with this kind of problem it's still possible)
  2. Building a tree of actions you want to do
  3. At the end of the string, you evaluate the tree and do some calculations

For example you have "1+2/3", this could evaluate to the following data structure:

     "+"
     /  \
   "1"  "/"
       /   \
     "2"   "3"

You could then traverse that structure from top to bottom and do the computations. At first you've got the "+", which has a 1 on the left side and some expression on the right side, so you have to evaluate that expression first. So you go to the "/" node, which has two numeric children. Knowing that, you can now compute 2/3 and replace the whole "/" node with the result of that. Now you can go up again and compute the result of the "+" node: 1 + 0.66. Now you replace that node with the result and all you've got left is the result of the expression.

Some pseudo code on how this might look in your code:

calculation(operator, leftValue, rightValue):
   switch operator {
      case '+': return leftValue + rightValue
      case '-': return 42
   }

action(node):
   node.value = calculation(node.operator, action(node.left) action(node.right))

As you might have noticed, the tree is designed in such a way that it honors operator precedence. The / has a lower level than the +, which means it get's evaluated first.

However you do this in detail, that's basically the way to go.

Sign up to request clarification or add additional context in comments.

2 Comments

Well, 1+2/3 will result to 1+(2/3) but not to (1+2)/3. So the answer will be 1.6(6) and not 3/3. It means that your math processor should also consider operator precedence.
Oh, err, yes. I meant to write it that way.
2

You can use the expression parser that is included in the math.js library:

http://mathjs.org

Example usage:

mathjs.evaluate('1.2 / (2.3 + 0.7)');   // 0.4
mathjs.evaluate('5.08 cm in inch');     // 2 inch
mathjs.evaluate('sin(45 deg) ^ 2');     // 0.5
mathjs.evaluate('9 / 3 + 2i');          // 3 + 2i
mathjs.evaluate('det([-1, 2; 3, 1])');  // -7

Comments

1

You can use eval safely for a simple arithmetic calculator by filtering the input- if you only accept digits, decimal points and operators (+,-,*,/) you won't get in much trouble. If you want advanced Math functions, you are better off with the parser suggestions.

function calculate(){
    "use strict";
    var s= prompt('Enter problem');
    if(/[^0-9()*+\/ .-]+/.test(s)) throw Error('bad input...');
    try{
        var ans= eval(s);
    }
    catch(er){
        alert(er.message);
    }
    alert(ans);
}

calculate()

Comments

0

I write some functions when I had a problem like this. Maybe this can help:

data = [
  {id:1,val1:"test",val2:"test2",val2:"test3"},
  {id:2,val1:"test",val2:"test2",val2:"test3"},
  {id:3,val1:"test",val2:"test2",val2:"test3"}
];
datakey = Object.keys(data[0]);
// here's a fix for e['datakey[f]'] >> e[x]
vix = function(e,f){
	a = "string";
	e[a] = datakey[f];
	x = e.string;
	end = e[x];
	delete e.string;
	return end;
};
// here's a fix to define that variable
vox = function(e,f,string){
	a = "string";
	e[a] = datakey[f];
	x = e.string;
	end = e[x] = string;
	delete e.string;
};
row = 2 // 3th row ==> {id:3,val1:"test",val2:"test2",val2:"test3"}
column = 1 //datakey 2 ==> val1
vox(data[row],column,"new value");
alert(data[2].val1); //the value that we have changed

Comments