To develop is to renounce!

Many users wanted to be able to use functions defined in the Graph application in the Calculation application, and this is one of the features we developed for the 9.2.0 update. We also took this opportunity to allow the renaming of functions and variables without (too many) constraints, and to make functions accessible from all applications.

We ran into a few problems when designing and developing this feature: we had to make some choices, here are two of them!

Variable or function?

When you press on exe after writing a calculation in the application Calculation, one of the first things the calculator does is to try to understand what you have written. If you write 13+2, the calculator first identifies the different ‘words’ in the calculation, which are the number 13, the symbol +, and the number 2. It then applies some grammar rules, just like you are doing right now to understand what you are reading: a number, followed by +, followed by a number, is the addition of the first number with the second.

Now add functions and variables that you can rename as you want: things get tougher. Let’s take an example: if you write abba(1+2), even you cannot be sure of what is meant, because the grammar is ambiguous.

  • Is it the function abba applied to 1+2?
  • Is the variable abba multiplied by 1+2?
  • Is it the variable a multiplied by the variable b, multiplied by the variable b, multiplied by the variable a, multiplied by 1+2?
  • Is it the variable abb multiplied by the function a applied to 1+2?

Interpret a function

We weighed the pros and cons of various ways to distinguish between variable and functions:

  1. Always create the longest variable name possible and, if it is followed by parentheses, it is a function name. This rule is simple, but the user will have to write multiplication signs where they could be implicit.
  2. Check if there are variables or functions defined by the user that would match the expression. For instance in abba(1+2), if abba is an existing variable or function, just interpret it like that. If abba does not exist but a and b are two defined variables, interpret the expression as a*b*b*a*(1+2). And so on… This method would better match the user intentions, but it is quite complex and might slow down the calculator.
  3. Mix the previous methods: always create the longest name, then check if it is a variable or a function and interpret it as such.

We chose to use the first method, because it is simpler and takes less computation time. It does not require much work from the user to make herself understood.

Circular definitions

You can define functions (or variables) that refer to each other, but this means the calculator needs to handle circular definitions, such as f(x)=g(x) and g(x)=f(x).

At first, we would handle the evaluation of f(1) (for instance) by replacing f with its definition, g(x), replace the unknown x with 1, and then evaluate this definition. By doing this, the calculator keeps in one of its memory areas, called the stack, the fact that it needs to evaluate g(1) to evaluate f(1). At the next step, the calculator keeps in memory that it is evaluating f(1) to evaluate g(1) to evaluate f(1). And so on, the calculator will at some point fill its memory and crash.

Circular definitions

Once again, there are many ways to solve this problem:

  1. Prevent the user from creating circular definitions. This solution adds many verifications for the calculator and limits the user’s actions. For instance, maybe the user defined f(x)=1 et g(x)=f(x)+1 and wants to change this to f(x)=g(x)+1 et g(x)=1. The user would be obliged to redefine g(x) before f(x), because the intermediary step f(x)=g(x)+1 et g(x)=f(x)+1 is forbidden.
  2. Limit the number of operations the calculator can do to evaluate an expression:
    • By putting a limit of 1000 operations, for instance. This is not difficult to develop, but choosing the maximal number of operations is tricky. Not all evaluations use the same amount of operations and we would not want to limit computations that could have succeeded, such as sum(n,0,10000).
    • By stopping the evaluation when the stack is too full. This is an interesting solution, but it is complicated to develop on the calculator’s web simulator and we want to tackle the problem on all of our platforms. The calculator would also compute for a long time before stopping.
  3. Stop evaluating a function if it needs to evaluate itself at some point. We would need to remember which functions/variables evaluations led to the current evaluation, which is doable! However, we cannot inspect the stack for this, because it is complicated in practice.
  4. When evaluating a function, replace it with its definition and keep replacing functions in the definition before evaluating anything, until there are no more replaceable functions. Limit the number of replacements the calculator is allowed to do, after which the function/variable is considered circularly defined.

Solutions 3. and 4. quickly interrupt a circular computation, without limiting long computations that can succeed. We chose solution 4. because it is simpler to develop and as satisfying as solution 3.

And many other choices…

These were two choices we made to allow you to use functions and variables in all applications, but there were many others. What should the calculator do if the user tries to create a variable/function which name is already taken? If a=x+1 and f(x)=a, what is the evaluation of f(3)? When creating the variable a=1+1+b, should it be stored as is, or stored as 2+b, or also replace b with its definition?

Explore the calculator, look out for our choices and do not hesitate to suggest ameliorations!