I am trying to build a calculator with Javscript, and have implemented all common operators. Now I would like to implement a factorial(!) operator.
I have a factorial(n) function defined, now I would like to replace the ! with my function and then use eval() on the resulting string and display the output.
I can implement it iterating backwards, but it does not work with brackets.
Here is my code -
function replaceFactorial(expression) {
expression = expression.replace(' ', '');
index = expression.indexOf('!');
number = '';
for (let i = index - 1; i >= 0; --i) {
if (!(['+', '-', '*', '/'].includes(expression.charAt(i)))) {
number += expression.charAt(i);
} else {
break;
}
}
console.log(number + '!', 'factorial(' + number + ')')
expression = expression.replace(number + '!', 'factorial(' + number + ')');
return eval(expression);
}
For example:
factorial('5! + 5') outputs 125. As expected, but
factorial('(2 + 3)!') does not work!
In my method, I check backwards from ! and once reaching a operator, stop and change the number. But it does not work when I have brackets.
How can I make it work?
Another idea is to define factorial as a prototype method of Number. The replacement becomes then quite trivial: every "!" can be replaced with ["factorial"]():
"use strict"; // To avoid this-wrapping overhead (optional)
Number.prototype.factorial = function () {
return this > 0 ? this * (this-1).factorial() : 1;
}
function evaluate(str) {
str = str.replaceAll("!", '["factorial"]()');
console.log(str); // Just to see what it generates
return eval(str);
}
console.log(evaluate("1 + (2*3)!")); // 721
console.log(evaluate("3!!-1")); // 619
console.log(evaluate("(2-2)!*3")); // 3
console.log(evaluate("'abc'.length!")); // 6
You could use a regular expression which looks for digits and a following bang, both grouped. Then take a function for calling factorial with the second argument.
function factorial(n) { return +!n || n * factorial(n - 1); }
const
string = '5! + 2!'
console.log(string.replace(/(\d+)(!)/g, (_, v) => factorial(v)));