如何将字符串转换为数学函数并仅转换一次?

4
我希望构建类似于Desmos的东西,用户可以在画布上绘制图形,然后移动它。
到目前为止,我已经成功了,但唯一剩下的是用户输入。
使用标签,我希望用户例如写入:
"5x + 2"

结果应该是:
var f = 5*x + 2;

我花了很多时间寻找一种方法来实现这一点,我发现的唯一可行的方法是使用JavaScript中的一些数学库和eval()函数。后者非常有用,因为我可以将图表中的x替换为x值,并且可以构建函数的图表。问题在于,当我想要移动图表时,它会出现很多延迟,因此这不是最好的办法。
我确定它会出现延迟,因为eval()函数必须每次将字符串转换为画布上每个x值的函数,大约每秒40-50次。
我想要实现的是将字符串转换为数学函数,然后仅使用它。
这可能吗?能否有人帮忙? 编辑1: 这是我的函数:
function f (pixelX) {
    var x = getCoordX (pixelX);

    var f = 2 * x + 2;

    return getPixelY(f);
}

1
为什么需要每秒转换40-50次?问题不在于“eval”。你正在问错问题。 - XCS
当我移动鼠标时,坐标系也会移动。我用一个计数变量测量它,大约每秒50次。 - user5730329
是的,问题在于如何重新绘制画布。eval是即时的,问题在于重新绘制画布所需的时间,这就是为什么会出现延迟的原因。尝试在requestAnimationFrame回调函数中调用你的“draw”函数。 - XCS
mathJS 的解析引擎可以完美地满足您的需求。 - Jamiec
2个回答

3
回答你的问题(尽管这不会解决你的问题)。
你可以这样做。
var myString = "5 * x + 2";
var f = Function("x", "return " + myString);

这将从一个字符串中创建一个函数。第一个参数是第一个参数的名称,即x,第二个参数是函数体(即return语句);
然后你可以这样调用它:f(3),结果将是17
请注意,在方程式中必须像5 * x这样写乘法,而不是像5x这样写。
这样一来,你只需要将字符串转换为函数一次,然后就可以使用不同的参数多次调用它。
你的问题不在于eval需要很长时间来计算,而在于重新绘制画布非常昂贵。尝试限制绘图数量或仅在requestAnimationFrame回调内调用绘图函数,这样浏览器只会在准备好时重新绘制画布。

1
只有在鼠标按下且移动时,画布才会重新绘制。我还没有限制绘图的数量,也不知道requestAnimationFrame是什么。我会尝试看看你的示例是否有效。 - user5730329
1
它确实起作用了,并且比eval()要好得多。这样你只需要将字符串评估为一个函数一次,然后你就可以根据需要使用不同的参数无限调用它。 这正是我所期望的。我会更深入地研究requestAnimationFrame。非常感谢。我会点赞并将您的答案标记为正确的。 ;) - user5730329

2

您可以使用eval函数,但它在每个浏览器的JavaScript引擎上的行为可能会有所不同。

这是一个简单的查找和替换:

function solveForX(equation, xValue) {
  var expanded = equation.replace(/(\d+(?:\.\d+|))x/g, '$1 * x');
  return eval(expanded.replace(/x/g, xValue));
}

console.log(solveForX("5x + 2", 3));       // 17
console.log(solveForX("-4.2x + 3x", 5));   // -6
.as-console-wrapper { top: 0; max-height: 100% !important; }

高级 — 多变量表达式

const expFormat = '(\\d+(?:\\.\\d+|)){{@}}';
var expressionCache = {};
function lookupExpansion(v) {
  if (!expressionCache[v]) {
    expressionCache[v] = new RegExp(expFormat.replace(/\{\{\@\}\}/, v), 'g');
  }
  return expressionCache[v];
}

function toFunction(equation, variables) {
  variables.forEach(variable => {
    equation = equation.replace(lookupExpansion(variable), '$1 * ' + variable);
  });
  equation = equation.replace(/\b([a-z])([a-z])\b/g, '$1 * $2');
  console.log('[DEBUG]: Expanded => ' + equation);
  return Function.apply(null, variables.concat('return ' + equation));
}

// ======================== Simple ============================== //
var simpleMultiVariableFn = toFunction("x + 4x + 2y", ['x', 'y']);
console.log(simpleMultiVariableFn(3, 5)); // 25

// ======================== Advanced ============================ //
var slopeInterceptFunction = (slope, yIntercept) => {
  return x => toFunction("mx + b", ['m', 'x', 'b']).call(null, slope, x, yIntercept);
};
var interceptFn = slopeInterceptFunction(1, 2); // Reusable!
console.log(interceptFn(3)); // 5
console.log(interceptFn(4)); // 6
.as-console-wrapper { top: 0; max-height: 100% !important; }

可以处理浮点数和负数。


1
谢谢你的回答。稍后会更深入地研究一下。;) - user5730329

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接