1

Main question:

Can I use a variable in replace of a Math. function that defines the math function at the end of the Math object so Math.variableName will be the same as Math.sin(x) (if variableName = 'sin(x)')?


I'm creating a simple math graphing calculator in JavaScript. I have a variable that stores a user's equation:

var userEquation = $('#userEquation');

and for this case, let's say the user typed: sin(x).

I want to be able to use the Math. object for any equation the user may put in, such as cos(x), or cos(x)/sin(x). This is the function that converts the user input to Math.userEquation.

function getEquation(x) {
  var userEquation = $('#userEquation').val(); // or pretty much "var userEquation = 'sin(x)'
  return Math.equation;
}

The function returns undefined, for there is no Math.userEquation. What does work is:

return Math.sin(x);

But this won't work if the user types a more complicated formula that's not already specified in my program. Is there a way to use the Math object with a variable?

EDIT: I did not want to use external libraries in this besides jQuery if at all possible. The other question similar to this one found here, used external libraries not directly built into JavaScript.

EDIT 2:

My graph function looks like this:

for (var i = iMin; i <= iMax; i++) {
    xx = dx * i;
    var formula = xx / scale;
    yy = scale * getEquation (formula); // Should be Math.sin(xx/scale) for getEquation
    if (i == iMin) ctx.moveTo(x +xx, y - yy);
    else ctx.lineTo(x + xx, y - yy);
}
ctx.stroke();

This function uses the equation function in the for loop for ex in order to find the points to plot, and it didn't work with external libraries.

I don't think that Math.sin(x)*cos(x) works as is on its own, will another method help solve this if it's in a variable as well?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
RATIU5
  • 368
  • 6
  • 20
  • 1
    This is not a duplicate in my opinion, it's not about evaluating a string as a math expression such as "1+1". Rather its about being able to take user input and then specify part of that input as the operation, and part as the operand, on the built in Math object, e.g. `Math[operation](operand)` Not very similar at all to the question it is marked as a duplicate of. Hopefully at the least the OP will be able to take my suggestion as a starting point. – Dexygen Oct 23 '18 at 17:02
  • @GeorgeJempty it might not be a duplicate, but it makes rather little sense to begin with IMHO, to try and do it this way. Translating input like `sin(x)` into the “corresponding” Math.sin call might still be a rather trivial thing, but when they are asking about “a more complicated formula”, which probably means multiple of such mathematical functions joined with arithmetic operators in between, it is going to need some sort of parser to take this apart in the first place. – misorude Nov 01 '18 at 12:52
  • @misorude Yes, My plan would be for it to accept equations that require more than one object sometimes, such as `sin(x)/cos(x)`. – RATIU5 Nov 01 '18 at 13:46
  • 2
    Well then you are going to need some sort of parser that takes this apart into its individual components to begin with. The other answers you found so far probably use external libraries specifically because this is not as trivial as you might initially think. – misorude Nov 01 '18 at 13:59

1 Answers1

1

For the sake of simplicity, I'm going to assume there is a function that converts the equation into a tree form for easy parsing. Since this question is not about parsing equations, I will leave this as an exercise for the OP. Therefore, let's just assume we have a class EquationTree with a constructor that takes an equation and returns a new EquationTree

EquationTree(equation: string): EquationTree

EquationTree is a class that represents the equation in a tree. For the sake of ease, I will represent EquationTrees using prefix notation; however, this is an implementation detail and easily modified. Here are a few examples of what an EquationTree may look like for various equations:

sin(x):

sin
 |
 x

sin(x) + cos(x):

    +
  /  \
sin  cos
 |    |
 x    x

sin(x) * cos(x) + tan(x) / floor(x):

        +
      /   \
    *      /
   / \    / \
sin  cos tan floor
 |    |   |    |
 x    x   x    x

Now, let's define the interface for EquationTree. From my understanding of the problem, you want to be able to evaluate the equation with various values of x. So, EquationTree only really needs one method:

evaluate(x: number): number

Now, instead of defining a new method on Math (called equation), we just pass around the EquationTree and tell it to evaluate itself for a given value of x. This also makes it a lot easier to save a history of the equations the user has entered. Just save a list of the EquationTrees.

So how does evaluate() work? That is the real question. Since we are using prefix notation, we need to perform a preorder traversal of the EquationTree. When we reach a leaf node, it will either be x or a numeric value. If it is an x, we replace it with the value we were provided. Internal nodes will be operators. If the operator is a simple operator (+, -, /, *), we can perform that operation ourselves. Otherwise, we'll want to assume the operator is a method on the Math object and execute the method on the node's children. This would look something like

switch (node.value) {
  case '+':
    return node.leftChild + node.rightChild;

  case '-':
    if (node.rightChild === null) {
      return -node.leftChild;
    }

    return node.leftChild - node.rightChild;

  case '/':
    return node.leftChild / node.rightChild;

  case '*':
    return node.leftChild * node.rightChild;

  default:
    try {
      Math[node.value](node.leftChild, node.rightChild);
    } catch (err) {
      // This operation is not defined on Math.  Handle it appropriately.
    }
}

As an example, let's say we generated the following tree

        +
      /   \
    *      /
   / \    / \
sin  cos tan floor
 |    |   |    |
 x    2   x    9

Then we were asked to evaluate the tree with the value 7 (equationTree.evaluate(7)). Evaluating this tree would look something like the following. Note, I am rounding and omitting steps where the value does not change.

Step 1:

        +
      /   \
    *      /
   / \    / \
sin  cos tan floor
 |    |   |    |
 7    2   x    9

Step 2:

        +
      /   \
    *      /
   / \    / \
0.12 cos tan floor
      |   |    |
      2   x    9

Step 3:

         +
      /     \
    *        /
   / \      / \
0.12 0.99 tan floor
           |    |
           x    9

Step 4:

     +
  /     \
0.12     /
        / \
      tan floor
       |    |
       x    9

Step 5:

     +
  /     \
0.12     /
        / \
      tan floor
       |    |
       7    9

Step 6:

     +
  /     \
0.12      /
        /   \
      0.12 floor
            |
            9

Step 7:

     +
  /     \
0.12      /
        /   \
      0.12   9

Step 8:

     +
  /     \
0.12    0.01

Step 9:

    0.13
c1moore
  • 1,827
  • 17
  • 27
  • For the sake of this example, I assume that operators that take > 2 operands can be split to form a binary operator. For example, `min(1, 2, 3)` is equivalent to `min(min(1, 2), 3)` – c1moore Nov 04 '18 at 13:15
  • This is a start to where I'm going, so I accepted the answer. – RATIU5 Nov 04 '18 at 14:07