Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- "use strict";
- const VARIABLES = new Map([
- ["x", 0],
- ["y", 1],
- ["z", 2]
- ]);
- const ZERO = new Const(0);
- const ONE = new Const(1);
- const TWO = new Const(2);
- function ExpressionFactory(func, funcName, diffAlg) {
- function Operation(...args) {
- this.operands = args;
- }
- Operation.prototype.toString = function () {
- return this.operands.join(" ") + " " + funcName;
- };
- Operation.prototype.evaluate = function (...args) {
- const result = this.operands.map((value) => value.evaluate(...args));
- return func(...result);
- };
- Operation.prototype.diff = function (name) {
- let differentials = this.operands.map((value) => value.diff(name));
- return diffAlg(...this.operands.concat(differentials));
- };
- Operation.prototype.prefix = function () {
- return "(" + funcName + ' ' + this.operands.map(function (value) {
- return value.prefix()
- }).join(" ") + ')';
- };
- Operation.prototype.postfix = function () {
- return "(" + this.operands.map(function (value) {
- return value.postfix()
- }).join(" ") + " " + funcName + ")";
- };
- return Operation;
- }
- function Const(value) {
- this.value = value;
- }
- Const.prototype.toString = function () {
- return this.value.toString();
- };
- Const.prototype.evaluate = function () {
- return this.value;
- };
- Const.prototype.diff = () => ZERO;
- Const.prototype.prefix = Const.prototype.toString;
- Const.prototype.postfix = Const.prototype.prefix;
- function Variable(name) {
- this.name = name;
- this.index = VARIABLES.get(name);
- }
- Variable.prototype.toString = function () {
- return this.name;
- };
- Variable.prototype.evaluate = function (...args) {
- return args[this.index];
- };
- Variable.prototype.diff = function (name) {
- return name === this.name ? ONE : ZERO;
- };
- Variable.prototype.prefix = function () {
- return this.name;
- };
- Variable.prototype.postfix = Variable.prototype.prefix;
- const Add = ExpressionFactory((x, y) => x + y, "+",
- (x, y, dx, dy) => new Add(dx, dy));
- const Subtract = ExpressionFactory((x, y) => x - y, "-",
- (x, y, dx, dy) => new Subtract(dx, dy));
- const Multiply = ExpressionFactory((x, y) => x * y, "*",
- (x, y, dx, dy) => new Add(new Multiply(dx, y), new Multiply(dy, x)));
- const Divide = ExpressionFactory((x, y) => x / y, "/",
- (x, y, dx, dy) => new Divide(new Subtract(new Multiply(dx, y), new Multiply(dy, x)), new Multiply(y, y)));
- const Negate = ExpressionFactory((x) => -x, "negate",
- (x, dx) => new Negate(dx));
- const Sqrt = ExpressionFactory(x => Math.sqrt(x), "sqrt",
- (x, dx) => new Divide(new Sqrt(dx), new Multiply(TWO, new Sqrt(x))));
- const Sum = ExpressionFactory((...args) => args.reduce((sum, current) => sum + current, 0),
- "sum", (...args) => new Sum(...args.slice(args.length / 2)));
- const Avg = ExpressionFactory((...args) => args.reduce((sum, current) => sum + current, 0) / args.length,
- "avg", (...args) => new Avg(...args.slice(args.length / 2)));
- const Sumexp = ExpressionFactory(
- (...args) => args.reduce((sum, current) => sum + Math.exp(current), 0),
- "sumexp",
- (...args) =>
- new Sum(...(args.slice(0, args.length / 2)).map(
- (v, ind) => new Multiply(args[ind + args.length / 2], new Sumexp(v))))
- );
- const Softmax = ExpressionFactory(
- (...args) => Math.exp(args[0]) / args.reduce((sum, current) => sum + Math.exp(current), 0),
- "softmax",
- (...args) =>
- new Divide(
- new Subtract(
- new Multiply(
- new Multiply(args[args.length / 2], new Sumexp(args[0])),
- new Sumexp(...(args.slice(0, args.length / 2)))),
- new Multiply(
- new Sumexp(args[0]),
- new Sum(...(args.slice(0, args.length / 2)).map(
- (v, ind) => new Multiply(args[ind + args.length / 2], new Sumexp(v))))
- )),
- new Multiply(
- new Sumexp(...(args.slice(0, args.length / 2))),
- new Sumexp(...(args.slice(0, args.length / 2))))
- )
- );
- const Sumsq = ExpressionFactory(
- (...args) => args.reduce((sum, current) => sum + Math.pow(current, 2), 0),
- "sumsq",
- (...args) => new Sum(...(args.slice(0, args.length / 2)).map(
- (v, ind) =>
- new Multiply(new Multiply(TWO, args[ind + args.length / 2]), v))));
- const Length = ExpressionFactory(
- (...args) => Math.sqrt(args.reduce((sum, current) => sum + Math.pow(current, 2), 0)),
- "length",
- (...args) => args.length === 0 ? ZERO :
- new Divide(
- new Sum(...(args.slice(0, args.length / 2)).map(
- (v, ind) => new Multiply(new Multiply(TWO, args[ind + args.length / 2]), v))),
- new Multiply(
- TWO,
- new Sqrt(new Sumsq(...(args.slice(0, args.length / 2))))
- )
- )
- );
- const OPERATIONS = new Map([
- ['+', [Add, 2]],
- ['-', [Subtract, 2]],
- ['*', [Multiply, 2]],
- ['/', [Divide, 2]],
- ['negate', [Negate, 1]],
- ['sqrt', [Sqrt, 1]],
- ['sum', [Sum, 0]],
- ['avg', [Avg, 0]],
- ['sumexp', [Sumexp, 0]],
- ['softmax', [Softmax, 0]],
- ['length', [Length, 0]],
- ['sumsq', [Sumsq, 0]]
- ]);
- const parse = expression => {
- let tokens = expression.trim().split(/\s+/);
- let stack = [];
- for (const token of tokens) {
- if (OPERATIONS.has(token)) {
- let operation = OPERATIONS.get(token)[0];
- let count = OPERATIONS.get(token)[1];
- stack.push(new operation(...stack.splice(stack.length - count, count)));
- } else if (VARIABLES.has(token)) {
- stack.push(new Variable(token));
- } else {
- stack.push(new Const(Number(token)));
- }
- }
- return stack.pop();
- };
- function ParserError(message) {
- this.message = message;
- }
- ParserError.prototype = Error.prototype;
- let mode;
- let expression;
- let pos = 0;
- function isDigit(operand) {
- return operand.length !== 0 && Number.isInteger(+operand);
- }
- function skipSpaces() {
- while (/\s/.test(expression[pos]) && pos < expression.length) {
- pos++
- }
- }
- function movePointer(object) {
- pos += object.toString().length;
- }
- function getObject() {
- let to = pos;
- while (!/\s/.test(expression[to]) && expression[to] !== ')' && expression[to] !== '(' && to < expression.length) {
- to++;
- }
- return expression.slice(pos, to);
- }
- function initialize(object) {
- if (VARIABLES.has(object)) {
- return new Variable(object);
- } else if (isDigit(object)) {
- return new Const((Number(object)));
- } else if (OPERATIONS.has(object)) {
- return object;
- } else {
- throw new ParserError("Unknown object: \"" + object + "\"");
- }
- }
- function parsePart() {
- let objectsStack = [];
- function getObjects() {
- skipSpaces();
- while (expression[pos] !== ')' && pos < expression.length) {
- skipSpaces();
- if (expression[pos] === '(') {
- objectsStack.push(parsePart());
- } else {
- let object = getObject();
- movePointer(object);
- objectsStack.push(initialize(object));
- }
- skipSpaces();
- }
- }
- skipSpaces();
- if (expression[pos] === '(') {
- pos++;
- getObjects();
- let operation = eval("objectsStack.".concat(mode));
- if (objectsStack.some(object => OPERATIONS.has(object))) {
- throw new ParserError("Operation among arguments")
- }
- if (expression[pos++] !== ')') {
- throw new ParserError("Missing )");
- }
- let func;
- if (OPERATIONS.has(operation)) {
- func = OPERATIONS.get(operation)[0];
- } else {
- throw new ParserError("Incorrect operation: " + "\"" + operation + "\"");
- }
- let expectedCount = OPERATIONS.get(operation)[1];
- if (expectedCount === objectsStack.length || OPERATIONS.get(operation)[1] === 0) {
- return new func(...objectsStack.splice(0));
- } else {
- throw new ParserError("Incorrect count of arguments. Expected: " + expectedCount + " met: " + objectsStack.length);
- }
- } else {
- getObjects();
- if (objectsStack.length !== 1) {
- throw new ParserError("Missing (");
- }
- }
- return objectsStack.pop();
- }
- function parseSide(s) {
- expression = s.trim();
- if (expression === '') {
- throw new ParserError("Empty input");
- }
- pos = 0;
- let answer = parsePart();
- if (pos < expression.length) {
- throw new ParserError("Excessive info")
- }
- return answer;
- }
- function parsePrefix(s) {
- mode = "shift()";
- return parseSide(s);
- }
- function parsePostfix(s) {
- mode = "pop()";
- return parseSide(s);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement