我正在尝试制作一个采用字符串公式的方法,并通过以非常小的间隔进行Riemann的求和来求解该公式的积分。我正在使用ScriptEngine和ScriptEngineManager类来评估函数(使用eval()方法)。出于某种原因,我收到了这个错误:
线程“main”中的异常java.lang.ClassCastException:java.lang.Integer无法强制转换为java.lang.Double 在sum.integral(sum.java:31) 在sum.main(sum.java:13)
import java.beans.Expression;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class sum {
//testing method
public static void main(String[] args) throws ScriptException {
double x = integral("5*x^2",0,5);
System.out.println(x);
}
public static double integral(String function, double lower, double upper) throws ScriptException
{
double total = 0;
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
//Solves function from upper to lower with a .001 interval, adding it to the total.
for (double i = lower; i < upper; i+=.001)
{
//evaluates the interval
engine.put("x",i);
total += (double)engine.eval(function);
}
return total;
}
}
答案 0 :(得分:3)
Nashorn使用optimistic typing(因为JDK 8u40),所以当不需要双精度时它将使用整数。因此,你不能指望它返回Double。
此外,5*x^2
表示&#34;五倍x xor 2&#34;在JavaScript中。 **
指数运算符是在较新版本的JavaScript语言中定义的,但Nashorn尚不支持它。
如果您将JavaScript代码更改为5*x*x
,那么它会起作用,但这样做会更安全:
total += 0.001 * ((Number)engine.eval(function)).doubleValue();
编译常用代码
由于您在循环中重复调用此函数,因此最佳做法是提前编译该函数。这种性能优化并不是绝对必要的,但因为引擎必须每次都编译你的函数(虽然它可能使用缓存来帮助它)。
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptContext;
CompiledScript compiledScript = ((Compilable)engine)
.compile("function func(x) { return " + function + "}");
compiledScript.eval(compiledScript.getEngine()
.getBindings(ScriptContext.ENGINE_SCOPE));
Invocable funcEngine = (Invocable) compiledScript.getEngine();
// . . .
total += 0.001 * ((Number)funcEngine.invokeFunction("func", i)).doubleValue();
使用ES6语言功能
将来,当Nashorn支持**
运营商时,如果您想使用它,您可能需要打开这样的ES6功能:
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
NashornScriptEngineFactory factory = new NashornScriptEngineFactory();
ScriptEngine enjin = factory.getScriptEngine("--language=es6");
或者像这样:
java -Dnashorn.args=--language=es6
*编辑以考虑评论中指出的数学修正。
答案 1 :(得分:2)
您的JS代码段返回Integer
(*),因为x^2
不是在JavaScript中获得2的幂的正确方法。请改为5*Math.pow(x,2)
,表达式将返回Double
。
在JavaScript中,^
运算符为bitwise XOR。
计算积分的循环也是错误的,你需要乘以矩形宽度:
double delta = 0.001;
for (double i = lower; i < upper; i += delta) {
//evaluates the interval
engine.put("x", i);
total += delta * ((Number) engine.eval(function)).doubleValue();
}
(*)有关初步解释,请参阅David's answer。但在评论中,@ A.Sundararajan提供了反对此的证据。我没有调查确切的原因,我只观察到我得到了Integer
,并且只是猜测在表达式中使用按位运算(来自OP的原始代码)触发了转换为整数。我最初编辑我的帖子以包含“数学错误”的修复,但大卫的新答案(大约4分钟^^)对于原始问题更完整,并且应该仍然是接受的答案恕我直言。