JSFiddle - React, Tailwind, and code Playground
by Josh Pullen
JavaScript
function evaluate([type, ...args]) {
switch (type) {
case "int":
return args[0];
case "add":
return args.map(evaluate).reduce((a, b) => a + b, 0);
case "multiply":
return args.map(evaluate).reduce((a, b) => a * b, 1);
}
}
function isExpr(expr) {
return Array.isArray(expr) && expr.length > 1 && typeof expr[0] === "string";
}
function isInt(value) {
return (expr) => {
if (!isExpr(expr)) return false;
const [type, ...args] = expr;
return type === "int" && args[0] === value;
}
}
function simplify([type, ...args]) {
args = args.map(arg => isExpr(arg) ? simplify(arg) : arg);
switch (type) {
case "add": {
if (!args.some(([type]) => type !== "int")) {
return simplify(["int", args.reduce((value, [type, ...args]) => value + args[0], 0)]);
}
break;
}
case "multiply": {
if (args.length === 1) {
return simplify(args[0]);
}
if (!args.some(([type]) => type !== "int")) {
return simplify(["int", args.reduce((value, [type, ...args]) => value * args[0], 1)]);
}
if (args.some(isInt(0))) {
return simplify(["int", 0]);
}
if (args.some(isInt(1))) {
return simplify(["multiply", ...args.filter(arg => !isInt(1)(arg))]);
}
break;
}
}
return [type, ...args];
}
function derivative(respecting = "x") {
const d = ([type, ...args]) => {
switch (type) {
case "int":
return ["int", 0];
case "add":
return ["add", ...args.map(d)];
case "multiply": {
let terms = [];
for (let i = 0; i < args.length; i++) {
let innerTerms = [];
for (let j = 0; j < args.length; j++) {
if (j === i) {
innerTerms.push(d(args[j]));
} else {
innerTerms.push(args[j]);
}
}
terms.push(["multiply", ...innerTerms]);
}
return ["add", ...terms];
}
case "variable":
if...