Quiero almacenar expresiones matemáticas arbitrarias con operaciones básicas (+, -, *, /, ^, sqrt, agrupación, etc.) y marcadores de posición de identificación como una cadena.
const json = { “formula”: “{{a45bc2a1-ed82-4ccd-a455-f7959e875aad}}+({{f6c2ef2b-a4fa-4cfb-b62d-d0d7c3e266d9}}*{{335563ad-a715-47b9-8e54-2b8553768168}})” }Los ID se asignan a matrices como:
const map = { “a45bc2a1-ed82-4ccd-a455-f7959e875aad”: [1, 2, 3, 4, 5], “f6c2ef2b-a4fa-4cfb-b62d-d0d7c3e266d9”: [10, 20, 30, 40, 50], “335563ad-a715-47b9-8e54-2b8553768168”: [1, 2, 3, 4, 5] }¿Cómo puedo conseguir esto? eval() no funciona con vectores y no sé cómo analizar fórmulas arbitrarias para realizar operaciones con elementos
El resultado debe ser:
[11, 42, 93, 164, 255]Dado que, de hecho, está desarrollando un lenguaje de programación, necesitará un compilador (=traducir expresiones de cadena en un AST) y un tiempo de ejecución (=evaluar AST entradas dadas y enlaces predefinidos). Aquí hay un código para empezar. Solo admite una expression = term | term op expression y no maneja ningún error:
// "compiler" function parse(str) { return expr([...str]) } function expr(chars) { let node = term(chars) if (chars.length) node = { op: chars.shift(), left: node, right: expr(chars) } return node } function term(chars) { let str = '' while (chars.length && chars[0].match(/\w/)) str += chars.shift() return {value: str} } // "runtime" ops = { '+': (a, b) => a + b, '-': (a, b) => a - b, } function evaluate(node, bindings) { if (node.value) return bindings[node.value] return eval_op( node.op, evaluate(node.left, bindings), evaluate(node.right, bindings)) } function eval_op(op, left, right) { let fn = ops[op] return left.map((_, i) => fn(left[i], right[i])) } // demo input = 'abc+def+xyz' bindings = { 'abc': [1, 2, 3], 'def': [4, 5, 6], 'xyz': [7, 8, 9], } ast = parse(input) console.log('AST', ast) res = evaluate(ast, bindings) console.log('RESULT', res)Por supuesto, hay mucho más trabajo involucrado para convertir este boceto en código real. Le sugiero que aprenda un poco sobre gramáticas formales, analizadores y generadores de analizadores.
Recorra los índices de la matriz y reemplace los marcadores de posición con el índice actual en el elemento correspondiente del map . Luego llame a eval() .
let len = Object.values(json.formula)[0].length; for (let i = 0; i < len; i++) { let formula = json.formula.replace(/{{([-\w]+)}}/g, (m, placeholder) => map[placeholder][i]; console.log(eval(formula)); }Jugué con esto un rato hoy y obtuve una versión que funciona que no requiere definir un AST formal, y solo usa eval () para instanciar una función de flecha que calcula cada índice de la salida en función de las listas de entrada y un índice de entrada incremental .
'use strict'; const json={ formula: '{{a45bc2a1-ed82-4ccd-a455-f7959e875aad}}+({{f6c2ef2b-a4fa-4cfb-b62d-d0d7c3e266d9}}*{{335563ad-a715-47b9-8e54-2b8553768168}})' } const map = { 'a45bc2a1-ed82-4ccd-a455-f7959e875aad': [1, 2, 3, 4, 5], 'f6c2ef2b-a4fa-4cfb-b62d-d0d7c3e266d9': [10, 20, 30, 40, 50], '335563ad-a715-47b9-8e54-2b8553768168': [1, 2, 3, 4, 5] } function parseFormula (formula) { const template = /{{([^}]*)}}/; const idList = []; for (let i = 0; template.test(formula); i++) { idList.push(formula.match(template)[0].replace(/[{}]/g, '')); formula = formula.replace(template, `input[${i}][i]`); } return {idList, formula}; } function calculateFormula(map, parsed) { const input = []; const result = []; const lambda = eval(`(input, i) => ${parsed.formula}`) parsed.idList.forEach(id => input.push(map[id])); for (let i = 0; i < input[0].length; i++) { result.push(lambda(input, i)); } return result; } const parsed = parseFormula(json.formula); console.log(parsed); console.log(calculateFormula(map, parsed));Producción:
{ idList: [ 'a45bc2a1-ed82-4ccd-a455-f7959e875aad', 'f6c2ef2b-a4fa-4cfb-b62d-d0d7c3e266d9', '335563ad-a715-47b9-8e54-2b8553768168' ], formula: 'input[0][i]+(input[1][i]*input[2][i])' } [ 11, 42, 93, 164, 255 ]