ฉันพยายามเขียนรูทีน Java เพื่อประเมินนิพจน์ทางคณิตศาสตร์อย่างง่ายจากString
ค่าเช่น:
"5+3"
"10-40"
"10*3"
ฉันต้องการหลีกเลี่ยงคำสั่ง if-then-else จำนวนมาก ฉันจะทำสิ่งนี้ได้อย่างไร
ฉันพยายามเขียนรูทีน Java เพื่อประเมินนิพจน์ทางคณิตศาสตร์อย่างง่ายจากString
ค่าเช่น:
"5+3"
"10-40"
"10*3"
ฉันต้องการหลีกเลี่ยงคำสั่ง if-then-else จำนวนมาก ฉันจะทำสิ่งนี้ได้อย่างไร
คำตอบ:
ด้วย JDK1.6 คุณสามารถใช้เครื่องมือ Javascript ในตัว
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
public class Test {
public static void main(String[] args) throws ScriptException {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String foo = "40+2";
System.out.println(engine.eval(foo));
}
}
return (Double) engine.eval(foo);
new javax.script.ScriptEngineManager().getEngineByName("JavaScript") .eval("var f = new java.io.FileWriter('hello.txt'); f.write('UNLIMITED POWER!'); f.close();");
- จะเขียนไฟล์ผ่านทาง JavaScript ไปยังไดเรกทอรีปัจจุบันของโปรแกรม (โดยค่าเริ่มต้น)
ฉันได้เขียนeval
วิธีการนี้สำหรับนิพจน์ทางคณิตศาสตร์เพื่อตอบคำถามนี้ มันจะบวกลบคูณหารยกกำลัง (โดยใช้^
สัญลักษณ์) sqrt
และฟังก์ชั่นพื้นฐานบางอย่างเช่น สนับสนุนการจัดกลุ่มโดยใช้(
... )
และได้รับความถูกต้องของผู้ปฏิบัติงานและกฎการเชื่อมโยงที่ถูกต้อง
public static double eval(final String str) {
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)`
// | number | functionName factor | factor `^` factor
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // addition
else if (eat('-')) x -= parseTerm(); // subtraction
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // multiplication
else if (eat('/')) x /= parseFactor(); // division
else return x;
}
}
double parseFactor() {
if (eat('+')) return parseFactor(); // unary plus
if (eat('-')) return -parseFactor(); // unary minus
double x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
eat(')');
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
x = parseFactor();
if (func.equals("sqrt")) x = Math.sqrt(x);
else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
else throw new RuntimeException("Unknown function: " + func);
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation
return x;
}
}.parse();
}
ตัวอย่าง:
System.out.println(eval("((4 - 2^3 + 1) * -sqrt(3*3+4*4)) / 2"));
เอาท์พุท: 7.5 (ซึ่งถูกต้อง)
parser เป็นตัวแยกวิเคราะห์สืบเชื้อสายซ้ำดังนั้นภายในใช้วิธีการแยกวิเคราะห์แยกต่างหากสำหรับแต่ละระดับของความสำคัญของผู้ประกอบการในไวยากรณ์ของมัน ฉันทำให้มันสั้นดังนั้นจึงง่ายต่อการปรับเปลี่ยน แต่นี่คือแนวคิดบางอย่างที่คุณอาจต้องการขยายด้วย:
ตัวแปร:
บิตของ parser ที่อ่านชื่อสำหรับฟังก์ชั่นสามารถจะเปลี่ยนไปเป็นตัวแปรที่กำหนดเองจับเกินไปโดยการมองขึ้นชื่อในตารางตัวแปรส่งผ่านไปยังวิธีการเช่นeval
Map<String,Double> variables
การรวบรวมและการประเมินแยก:
ถ้าหากมีการเพิ่มการรองรับตัวแปรคุณต้องการประเมินการแสดงออกหลายล้านครั้งด้วยตัวแปรที่เปลี่ยนแปลงโดยไม่ต้องแยกวิเคราะห์ทุกครั้งหรือไม่ มันเป็นไปได้. ก่อนกำหนดอินเทอร์เฟซที่ใช้เพื่อประเมินนิพจน์ที่คอมไพล์แล้ว:
@FunctionalInterface
interface Expression {
double eval();
}
ตอนนี้เปลี่ยนวิธีการทั้งหมดที่คืนค่าdouble
s ดังนั้นพวกเขากลับตัวอย่างของอินเทอร์เฟซที่ ไวยากรณ์แลมบ์ดาของ Java 8 นั้นยอดเยี่ยมสำหรับเรื่องนี้ ตัวอย่างหนึ่งในวิธีการที่เปลี่ยนแปลง:
Expression parseExpression() {
Expression x = parseTerm();
for (;;) {
if (eat('+')) { // addition
Expression a = x, b = parseTerm();
x = (() -> a.eval() + b.eval());
} else if (eat('-')) { // subtraction
Expression a = x, b = parseTerm();
x = (() -> a.eval() - b.eval());
} else {
return x;
}
}
}
ที่สร้างแผนภูมิแบบเรียกซ้ำของExpression
วัตถุที่แสดงถึงการแสดงออกที่รวบรวม ( ต้นไม้ไวยากรณ์นามธรรม ) จากนั้นคุณสามารถรวบรวมได้ครั้งเดียวและประเมินมันซ้ำ ๆ ด้วยค่าที่ต่างกัน:
public static void main(String[] args) {
Map<String,Double> variables = new HashMap<>();
Expression exp = parse("x^2 - x + 2", variables);
for (double x = -20; x <= +20; x++) {
variables.put("x", x);
System.out.println(x + " => " + exp.eval());
}
}
ประเภทข้อมูลที่แตกต่างกัน:
แทนที่จะdouble
คุณสามารถเปลี่ยนผู้ประเมินที่จะใช้สิ่งที่มีประสิทธิภาพมากขึ้นเช่นBigDecimal
หรือระดับที่ดำเนินตัวเลขที่ซับซ้อนหรือตัวเลขที่มีเหตุผล (เศษส่วน) คุณยังสามารถใช้Object
เพื่ออนุญาตให้มีประเภทข้อมูลบางส่วนในการแสดงออกเช่นภาษาโปรแกรมจริง :)
รหัสทั้งหมดในคำตอบนี้ปล่อยออกมาเพื่อเป็นสาธารณสมบัติ มีความสุข!
double x = parseTerm();
ประเมินผู้ประกอบการด้านซ้ายหลังจากนี้for (;;) {...}
ประเมินการดำเนินงานที่ประสบความสำเร็จของระดับการสั่งซื้อที่เกิดขึ้นจริง (นอกจากนี้การลบ) ตรรกะเดียวกันและในวิธี parseTerm parseFactor ไม่มีระดับถัดไปดังนั้นจึงมีเพียงการประเมินเมธอด / ตัวแปรหรือในกรณีของ paranthesis - ประเมินการแสดงออกของย่อย boolean eat(int charToEat)
วิธีการตรวจสอบความเท่าเทียมกันของตัวละครเคอร์เซอร์ปัจจุบันที่มีตัวละคร charToEat ถ้ากลับมาเท่ากันจริงและย้ายเคอร์เซอร์ไปยังตัวอักษรถัดไปผมใช้ชื่อ 'ยอมรับ' สำหรับมัน
วิธีที่ถูกต้องในการแก้ปัญหานี้อยู่กับlexerและparser คุณสามารถเขียนเวอร์ชันแบบง่าย ๆ เหล่านี้เองหรือหน้าเหล่านั้นยังมีลิงค์ไปยัง Java lexers และ parsers
การสร้าง parser ที่สืบเชื้อสายแบบเรียกซ้ำเป็นแบบฝึกหัดการเรียนรู้ที่ดีจริงๆ
สำหรับโครงการในมหาวิทยาลัยของฉันฉันกำลังมองหา parser / ผู้ประเมินที่สนับสนุนทั้งสูตรพื้นฐานและสมการที่ซับซ้อนมากขึ้น ฉันพบไลบรารี่โอเพนซอร์สที่ดีมากสำหรับ JAVA และ. NET ที่เรียกว่า mXparser ฉันจะให้ตัวอย่างบางอย่างเพื่อให้ความรู้สึกเกี่ยวกับไวยากรณ์สำหรับคำแนะนำเพิ่มเติมโปรดเยี่ยมชมเว็บไซต์โครงการ (โดยเฉพาะอย่างยิ่งส่วนการสอน)
https://mathparser.org/mxparser-tutorial/
และตัวอย่างเล็ก ๆ น้อย ๆ
1 - furmula ง่าย
Expression e = new Expression("( 2 + 3/4 + sin(pi) )/2");
double v = e.calculate()
2 - อาร์กิวเมนต์ที่ผู้ใช้กำหนดเองและค่าคงที่
Argument x = new Argument("x = 10");
Constant a = new Constant("a = pi^2");
Expression e = new Expression("cos(a*x)", x, a);
double v = e.calculate()
3 - ฟังก์ชั่นที่ผู้ใช้กำหนด
Function f = new Function("f(x, y, z) = sin(x) + cos(y*z)");
Expression e = new Expression("f(3,2,5)", f);
double v = e.calculate()
4 - วนซ้ำ
Expression e = new Expression("sum( i, 1, 100, sin(i) )");
double v = e.calculate()
พบเมื่อเร็ว ๆ นี้ - ในกรณีที่คุณต้องการลองใช้ไวยากรณ์ (และดูกรณีการใช้งานขั้นสูง) คุณสามารถดาวน์โหลดแอปScalar Calculator ที่ขับเคลื่อนโดย mXparser
ขอแสดงความนับถืออย่างสูง
ที่นี่เป็นอีกหนึ่งไลบรารีโอเพนซอร์ซบน GitHub ชื่อ EvalEx
ไลบรารีนี้มีจุดเน้นในการประเมินนิพจน์ทางคณิตศาสตร์ต่างจากเครื่องมือ JavaScript เท่านั้น นอกจากนี้ไลบรารียังขยายได้และสนับสนุนการใช้ตัวดำเนินการบูลีนและวงเล็บ
คุณสามารถลองใช้BeanShell interpreter:
Interpreter interpreter = new Interpreter();
interpreter.eval("result = (7+21*6)/(32-27)");
System.out.println(interpreter.get("result"));
คุณสามารถประเมินนิพจน์ได้อย่างง่ายดายหากแอปพลิเคชัน Java ของคุณเข้าถึงฐานข้อมูลอยู่แล้วโดยไม่ต้องใช้ JAR อื่น ๆ
ฐานข้อมูลบางอย่างต้องการให้คุณใช้ตารางจำลอง (เช่นตาราง "คู่" ของ Oracle) และฐานข้อมูลอื่นจะอนุญาตให้คุณประเมินนิพจน์โดยไม่ต้อง "เลือก" จากตารางใด ๆ
ตัวอย่างเช่นใน SQL Server หรือ Sqlite
select (((12.10 +12.0))/ 233.0) amount
และใน Oracle
select (((12.10 +12.0))/ 233.0) amount from dual;
ข้อดีของการใช้ฐานข้อมูลคือคุณสามารถประเมินหลาย ๆ นิพจน์ได้ในเวลาเดียวกัน นอกจากนี้ฐานข้อมูลส่วนใหญ่ยังอนุญาตให้คุณใช้นิพจน์ที่มีความซับซ้อนสูงและยังมีฟังก์ชั่นพิเศษจำนวนมากที่สามารถเรียกใช้ได้ตามความจำเป็น
อย่างไรก็ตามประสิทธิภาพการทำงานอาจประสบถ้านิพจน์เดียวจำนวนมากจำเป็นต้องประเมินเป็นรายบุคคลโดยเฉพาะอย่างยิ่งเมื่อ DB อยู่บนเซิร์ฟเวอร์เครือข่าย
ต่อไปนี้แก้ไขปัญหาด้านประสิทธิภาพในระดับหนึ่งโดยใช้ฐานข้อมูลในหน่วยความจำ Sqlite
นี่คือตัวอย่างการทำงานเต็มรูปแบบใน Java
Class. forName("org.sqlite.JDBC");
Connection conn = DriverManager.getConnection("jdbc:sqlite::memory:");
Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount");
rs.next();
System.out.println(rs.getBigDecimal(1));
stat.close();
conn.close();
แน่นอนคุณสามารถขยายรหัสข้างต้นเพื่อจัดการกับการคำนวณหลายรายการในเวลาเดียวกัน
ResultSet rs = stat.executeQuery( "select (1+10)/20.0 amount, (1+100)/20.0 amount2");
บทความนี้กล่าวถึงวิธีการต่าง ๆ นี่คือวิธีการ 2 วิธีที่กล่าวถึงในบทความ:
อนุญาตสำหรับสคริปต์ที่มีการอ้างอิงถึงวัตถุ Java
// Create or retrieve a JexlEngine
JexlEngine jexl = new JexlEngine();
// Create an expression object
String jexlExp = "foo.innerFoo.bar()";
Expression e = jexl.createExpression( jexlExp );
// Create a context and add data
JexlContext jctx = new MapContext();
jctx.set("foo", new Foo() );
// Now evaluate the expression, getting the result
Object o = e.evaluate(jctx);
private static void jsEvalWithVariable()
{
List<String> namesList = new ArrayList<String>();
namesList.add("Jill");
namesList.add("Bob");
namesList.add("Laureen");
namesList.add("Ed");
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
jsEngine.put("namesListKey", namesList);
System.out.println("Executing in script environment...");
try
{
jsEngine.eval("var x;" +
"var names = namesListKey.toArray();" +
"for(x in names) {" +
" println(names[x]);" +
"}" +
"namesListKey.add(\"Dana\");");
}
catch (ScriptException ex)
{
ex.printStackTrace();
}
}
อีกวิธีหนึ่งคือการใช้ Spring Expression Language หรือ SpEL ซึ่งทำงานได้มากขึ้นพร้อมกับการประเมินการแสดงออกทางคณิตศาสตร์ดังนั้นอาจจะเกินความจำเป็นเล็กน้อย คุณไม่จำเป็นต้องใช้ Spring Framework เพื่อใช้ไลบรารีนิพจน์นี้เนื่องจากเป็นแบบสแตนด์อะโลน คัดลอกตัวอย่างจากเอกสารของ SpEL:
ExpressionParser parser = new SpelExpressionParser();
int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); //24.0
อ่านตัวอย่าง SpEL ที่กระชับยิ่งขึ้นที่นี่และเอกสารฉบับสมบูรณ์ที่นี่
ถ้าเราจะใช้มันเราสามารถใช้อัลกอริทึมด้านล่าง: -
ในขณะที่ยังคงมีโทเค็นให้อ่าน
1.1 รับโทเค็นถัดไป 1.2 ถ้าโทเค็นคือ:
1.2.1 ตัวเลข: กดลงบนสแต็กค่า
1.2.2 ตัวแปร: รับค่าของมันแล้วกดลงบนสแต็กค่า
1.2.3 วงเล็บซ้าย: กดลงบนสแต็กโอเปอเรเตอร์
1.2.4 วงเล็บขวา:
1 While the thing on top of the operator stack is not a
left parenthesis,
1 Pop the operator from the operator stack.
2 Pop the value stack twice, getting two operands.
3 Apply the operator to the operands, in the correct order.
4 Push the result onto the value stack.
2 Pop the left parenthesis from the operator stack, and discard it.
1.2.5 ผู้ดำเนินการ (เรียกว่า thisOp):
1 While the operator stack is not empty, and the top thing on the
operator stack has the same or greater precedence as thisOp,
1 Pop the operator from the operator stack.
2 Pop the value stack twice, getting two operands.
3 Apply the operator to the operands, in the correct order.
4 Push the result onto the value stack.
2 Push thisOp onto the operator stack.
ในขณะที่โอเปอเรเตอร์สแต็กไม่ว่างเปล่า 1 นำโอเปอเรเตอร์จากสแต็กผู้ประกอบการ 2 เปิดสแต็กมูลค่าสองครั้งเพื่อรับสองตัวถูกดำเนินการ 3 ใช้โอเปอเรเตอร์กับตัวถูกดำเนินการตามลำดับที่ถูกต้อง 4 ดันผลลัพธ์ลงบนสแต็กค่า
ณ จุดนี้สแต็กโอเปอเรเตอร์ควรว่างเปล่าและสแต็กค่าควรมีเพียงหนึ่งค่าเท่านั้นซึ่งเป็นผลลัพธ์สุดท้าย
นี่เป็นอีกทางเลือกหนึ่งที่น่าสนใจ https://github.com/Shy-Ta/expression-evaluator-demo
การใช้งานง่ายมากและทำให้งานสำเร็จตัวอย่างเช่น
ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2");
assertEquals(BigDecimal.valueOf(11), evalExpr.eval());
ดูเหมือนว่าJEPควรทำงาน
ฉันคิดว่าสิ่งที่คุณทำเช่นนี้มันจะเกี่ยวข้องกับข้อความที่มีเงื่อนไขมากมาย แต่สำหรับการดำเนินงานเดี่ยวอย่างในตัวอย่างของคุณคุณสามารถ จำกัด ไว้ที่ 4 หากคำสั่งที่มีบางอย่างเช่น
String math = "1+4";
if (math.split("+").length == 2) {
//do calculation
} else if (math.split("-").length == 2) {
//do calculation
} ...
มันมีความซับซ้อนมากยิ่งขึ้นเมื่อคุณต้องการจัดการกับการทำงานหลายอย่างเช่น "4 + 5 * 6"
หากคุณกำลังพยายามสร้างเครื่องคิดเลขฉันก็จะผ่านการแยกส่วนการคำนวณ (แต่ละหมายเลขหรือโอเปอเรเตอร์) มากกว่าที่จะเป็นสตริงเดียว
มันสายเกินไปที่จะตอบ แต่ฉันเจอสถานการณ์เดียวกันเพื่อประเมินการแสดงออกใน java มันอาจช่วยใครซักคน
MVEL
ทำการประเมินผลนิพจน์ในรันไทม์เราสามารถเขียนโค้ดจาวาString
เพื่อให้ได้รับการประเมินในสิ่งนี้
String expressionStr = "x+y";
Map<String, Object> vars = new HashMap<String, Object>();
vars.put("x", 10);
vars.put("y", 20);
ExecutableStatement statement = (ExecutableStatement) MVEL.compileExpression(expressionStr);
Object result = MVEL.executeExpression(statement, vars);
คุณอาจดูกรอบ Symja :
ExprEvaluator util = new ExprEvaluator();
IExpr result = util.evaluate("10-40");
System.out.println(result.toString()); // -> "-30"
โปรดทราบว่าสามารถประเมินนิพจน์ที่ซับซ้อนได้มากขึ้นอย่างแน่นอน
// D(...) gives the derivative of the function Sin(x)*Cos(x)
IAST function = D(Times(Sin(x), Cos(x)), x);
IExpr result = util.evaluate(function);
// print: Cos(x)^2-Sin(x)^2
ลองโค้ดตัวอย่างต่อไปนี้โดยใช้เครื่องมือ Javascript ของ JDK1.6 พร้อมการจัดการการฉีดโค้ด
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class EvalUtil {
private static ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
public static void main(String[] args) {
try {
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || 5 >3 "));
System.out.println((new EvalUtil()).eval("(((5+5)/2) > 5) || true"));
} catch (Exception e) {
e.printStackTrace();
}
}
public Object eval(String input) throws Exception{
try {
if(input.matches(".*[a-zA-Z;~`#$_{}\\[\\]:\\\\;\"',\\.\\?]+.*")) {
throw new Exception("Invalid expression : " + input );
}
return engine.eval(input);
} catch (Exception e) {
e.printStackTrace();
throw e;
}
}
}
นี่เป็นคำตอบที่ได้รับจาก @Boann มีข้อผิดพลาดเล็กน้อยซึ่งทำให้ "-2 ^ 2" ให้ผลลัพธ์ที่ผิดพลาดของ -4.0 ปัญหาที่เกิดขึ้นคือประเด็นที่มีการประเมินการยกกำลังในของเขา เพียงแค่ย้ายการยกกำลังไปที่บล็อกของ parseTerm () และคุณจะไม่เป็นไร ดูด้านล่างซึ่งเป็นคำตอบของ @ Boann ที่แก้ไขเล็กน้อย การปรับเปลี่ยนอยู่ในความคิดเห็น
public static double eval(final String str) {
return new Object() {
int pos = -1, ch;
void nextChar() {
ch = (++pos < str.length()) ? str.charAt(pos) : -1;
}
boolean eat(int charToEat) {
while (ch == ' ') nextChar();
if (ch == charToEat) {
nextChar();
return true;
}
return false;
}
double parse() {
nextChar();
double x = parseExpression();
if (pos < str.length()) throw new RuntimeException("Unexpected: " + (char)ch);
return x;
}
// Grammar:
// expression = term | expression `+` term | expression `-` term
// term = factor | term `*` factor | term `/` factor
// factor = `+` factor | `-` factor | `(` expression `)`
// | number | functionName factor | factor `^` factor
double parseExpression() {
double x = parseTerm();
for (;;) {
if (eat('+')) x += parseTerm(); // addition
else if (eat('-')) x -= parseTerm(); // subtraction
else return x;
}
}
double parseTerm() {
double x = parseFactor();
for (;;) {
if (eat('*')) x *= parseFactor(); // multiplication
else if (eat('/')) x /= parseFactor(); // division
else if (eat('^')) x = Math.pow(x, parseFactor()); //exponentiation -> Moved in to here. So the problem is fixed
else return x;
}
}
double parseFactor() {
if (eat('+')) return parseFactor(); // unary plus
if (eat('-')) return -parseFactor(); // unary minus
double x;
int startPos = this.pos;
if (eat('(')) { // parentheses
x = parseExpression();
eat(')');
} else if ((ch >= '0' && ch <= '9') || ch == '.') { // numbers
while ((ch >= '0' && ch <= '9') || ch == '.') nextChar();
x = Double.parseDouble(str.substring(startPos, this.pos));
} else if (ch >= 'a' && ch <= 'z') { // functions
while (ch >= 'a' && ch <= 'z') nextChar();
String func = str.substring(startPos, this.pos);
x = parseFactor();
if (func.equals("sqrt")) x = Math.sqrt(x);
else if (func.equals("sin")) x = Math.sin(Math.toRadians(x));
else if (func.equals("cos")) x = Math.cos(Math.toRadians(x));
else if (func.equals("tan")) x = Math.tan(Math.toRadians(x));
else throw new RuntimeException("Unknown function: " + func);
} else {
throw new RuntimeException("Unexpected: " + (char)ch);
}
//if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation -> This is causing a bit of problem
return x;
}
}.parse();
}
-2^2 = -4
เป็นเรื่องปกติและไม่ใช่ข้อผิดพลาด -(2^2)
จะได้รับการจัดกลุ่มเช่น ลองใช้กับ Desmos ตัวอย่างเช่น รหัสของคุณแนะนำข้อบกพร่องหลายอย่าง ประการแรกคือ^
ไม่มีกลุ่มจากขวาไปซ้ายอีกต่อไป ในคำอื่น ๆ2^3^2
ที่ควรจะเป็นกลุ่มที่ชอบ2^(3^2)
เพราะ^
เป็นขวาเชื่อมโยง (2^3)^2
แต่การปรับเปลี่ยนของคุณทำให้กลุ่มเช่น อย่างที่สองก็^
คือควรจะมีลำดับความสำคัญสูงกว่า*
และ/
แต่การดัดแปลงของคุณจะเป็นแบบเดียวกัน ดูideone.com/iN2mMa
package ExpressionCalculator.expressioncalculator;
import java.text.DecimalFormat;
import java.util.Scanner;
public class ExpressionCalculator {
private static String addSpaces(String exp){
//Add space padding to operands.
//https://regex101.com/r/sJ9gM7/73
exp = exp.replaceAll("(?<=[0-9()])[\\/]", " / ");
exp = exp.replaceAll("(?<=[0-9()])[\\^]", " ^ ");
exp = exp.replaceAll("(?<=[0-9()])[\\*]", " * ");
exp = exp.replaceAll("(?<=[0-9()])[+]", " + ");
exp = exp.replaceAll("(?<=[0-9()])[-]", " - ");
//Keep replacing double spaces with single spaces until your string is properly formatted
/*while(exp.indexOf(" ") != -1){
exp = exp.replace(" ", " ");
}*/
exp = exp.replaceAll(" {2,}", " ");
return exp;
}
public static Double evaluate(String expr){
DecimalFormat df = new DecimalFormat("#.####");
//Format the expression properly before performing operations
String expression = addSpaces(expr);
try {
//We will evaluate using rule BDMAS, i.e. brackets, division, power, multiplication, addition and
//subtraction will be processed in following order
int indexClose = expression.indexOf(")");
int indexOpen = -1;
if (indexClose != -1) {
String substring = expression.substring(0, indexClose);
indexOpen = substring.lastIndexOf("(");
substring = substring.substring(indexOpen + 1).trim();
if(indexOpen != -1 && indexClose != -1) {
Double result = evaluate(substring);
expression = expression.substring(0, indexOpen).trim() + " " + result + " " + expression.substring(indexClose + 1).trim();
return evaluate(expression.trim());
}
}
String operation = "";
if(expression.indexOf(" / ") != -1){
operation = "/";
}else if(expression.indexOf(" ^ ") != -1){
operation = "^";
} else if(expression.indexOf(" * ") != -1){
operation = "*";
} else if(expression.indexOf(" + ") != -1){
operation = "+";
} else if(expression.indexOf(" - ") != -1){ //Avoid negative numbers
operation = "-";
} else{
return Double.parseDouble(expression);
}
int index = expression.indexOf(operation);
if(index != -1){
indexOpen = expression.lastIndexOf(" ", index - 2);
indexOpen = (indexOpen == -1)?0:indexOpen;
indexClose = expression.indexOf(" ", index + 2);
indexClose = (indexClose == -1)?expression.length():indexClose;
if(indexOpen != -1 && indexClose != -1) {
Double lhs = Double.parseDouble(expression.substring(indexOpen, index));
Double rhs = Double.parseDouble(expression.substring(index + 2, indexClose));
Double result = null;
switch (operation){
case "/":
//Prevent divide by 0 exception.
if(rhs == 0){
return null;
}
result = lhs / rhs;
break;
case "^":
result = Math.pow(lhs, rhs);
break;
case "*":
result = lhs * rhs;
break;
case "-":
result = lhs - rhs;
break;
case "+":
result = lhs + rhs;
break;
default:
break;
}
if(indexClose == expression.length()){
expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose);
}else{
expression = expression.substring(0, indexOpen) + " " + result + " " + expression.substring(indexClose + 1);
}
return Double.valueOf(df.format(evaluate(expression.trim())));
}
}
}catch(Exception exp){
exp.printStackTrace();
}
return 0.0;
}
public static void main(String args[]){
Scanner scanner = new Scanner(System.in);
System.out.print("Enter an Mathematical Expression to Evaluate: ");
String input = scanner.nextLine();
System.out.println(evaluate(input));
}
}
เกี่ยวกับบางสิ่งเช่นนี้:
String st = "10+3";
int result;
for(int i=0;i<st.length();i++)
{
if(st.charAt(i)=='+')
{
result=Integer.parseInt(st.substring(0, i))+Integer.parseInt(st.substring(i+1, st.length()));
System.out.print(result);
}
}
และทำสิ่งที่คล้ายกันสำหรับตัวดำเนินการทางคณิตศาสตร์อื่น ๆ ตามลำดับ ..
มันเป็นไปได้ที่จะแปลงสตริงการแสดงออกใด ๆ ในมัดสัญกรณ์ไป postfix สัญกรณ์ใช้ขั้นตอนวิธีการแบ่งหลา Djikstra ของ ผลลัพธ์ของอัลกอริทึมสามารถใช้เป็นอินพุตไปยังอัลกอริทึม postfixโดยส่งคืนผลลัพธ์ของนิพจน์
ฉันเขียนบทความเกี่ยวกับที่นี่ด้วยการใช้งานใน java
อีกตัวเลือกหนึ่ง: https://github.com/stefaninatingein/expressionparser
ฉันใช้สิ่งนี้เพื่อให้มีตัวเลือกที่เรียบง่าย แต่มีความยืดหยุ่นในการอนุญาตทั้งสอง:
TreeBuilder ที่ลิงก์ด้านบนเป็นส่วนหนึ่งของแพ็คเกจการสาธิต CASซึ่งได้รับสัญลักษณ์ นอกจากนี้ยังมีตัวอย่างล่ามพื้นฐานและฉันได้เริ่มสร้างล่าม TypeScriptใช้มัน
คลาส Java ที่สามารถประเมินนิพจน์ทางคณิตศาสตร์:
package test;
public class Calculator {
public static Double calculate(String expression){
if (expression == null || expression.length() == 0) {
return null;
}
return calc(expression.replace(" ", ""));
}
public static Double calc(String expression) {
if (expression.startsWith("(") && expression.endsWith(")")) {
return calc(expression.substring(1, expression.length() - 1));
}
String[] containerArr = new String[]{expression};
double leftVal = getNextOperand(containerArr);
expression = containerArr[0];
if (expression.length() == 0) {
return leftVal;
}
char operator = expression.charAt(0);
expression = expression.substring(1);
while (operator == '*' || operator == '/') {
containerArr[0] = expression;
double rightVal = getNextOperand(containerArr);
expression = containerArr[0];
if (operator == '*') {
leftVal = leftVal * rightVal;
} else {
leftVal = leftVal / rightVal;
}
if (expression.length() > 0) {
operator = expression.charAt(0);
expression = expression.substring(1);
} else {
return leftVal;
}
}
if (operator == '+') {
return leftVal + calc(expression);
} else {
return leftVal - calc(expression);
}
}
private static double getNextOperand(String[] exp){
double res;
if (exp[0].startsWith("(")) {
int open = 1;
int i = 1;
while (open != 0) {
if (exp[0].charAt(i) == '(') {
open++;
} else if (exp[0].charAt(i) == ')') {
open--;
}
i++;
}
res = calc(exp[0].substring(1, i - 1));
exp[0] = exp[0].substring(i);
} else {
int i = 1;
if (exp[0].charAt(0) == '-') {
i++;
}
while (exp[0].length() > i && isNumber((int) exp[0].charAt(i))) {
i++;
}
res = Double.parseDouble(exp[0].substring(0, i));
exp[0] = exp[0].substring(i);
}
return res;
}
private static boolean isNumber(int c) {
int zero = (int) '0';
int nine = (int) '9';
return (c >= zero && c <= nine) || c =='.';
}
public static void main(String[] args) {
System.out.println(calculate("(((( -6 )))) * 9 * -1"));
System.out.println(calc("(-5.2+-5*-5*((5/4+2)))"));
}
}
ไลบรารี่ภายนอกเช่น RHINO หรือ NASHORN สามารถใช้เพื่อเรียกใช้จาวาสคริปต์ และจาวาสคริปต์สามารถประเมินสูตรง่ายๆได้โดยไม่ต้องคัดลอกสตริง ไม่มีผลกระทบต่อประสิทธิภาพเช่นกันหากเขียนโค้ดได้ดี ด้านล่างเป็นตัวอย่างของ RHINO -
public class RhinoApp {
private String simpleAdd = "(12+13+2-2)*2+(12+13+2-2)*2";
public void runJavaScript() {
Context jsCx = Context.enter();
Context.getCurrentContext().setOptimizationLevel(-1);
ScriptableObject scope = jsCx.initStandardObjects();
Object result = jsCx.evaluateString(scope, simpleAdd , "formula", 0, null);
Context.exit();
System.out.println(result);
}
import java.util.*;
public class check {
int ans;
String str="7 + 5";
StringTokenizer st=new StringTokenizer(str);
int v1=Integer.parseInt(st.nextToken());
String op=st.nextToken();
int v2=Integer.parseInt(st.nextToken());
if(op.equals("+")) { ans= v1 + v2; }
if(op.equals("-")) { ans= v1 - v2; }
//.........
}