Saya baru-baru ini menulis parser ekspresi matematika bernama exp4j yang dirilis di bawah lisensi apache Anda dapat memeriksanya di sini: objecthunter.net/exp4j
fasseg
2
Apa jenis ekspresi yang Anda izinkan? Hanya ekspresi operator tunggal? Apakah tanda kurung diizinkan?
Sepertinya ada masalah besar di sana; Itu mengeksekusi skrip, bukan mengevaluasi ekspresi. Agar jelas, engine.eval ("8; 40 + 2"), output 42! Jika Anda ingin parser ekspresi yang juga memeriksa sintaks, saya baru saja menyelesaikan satu (karena saya tidak menemukan apa pun yang sesuai dengan kebutuhan saya): Javaluator .
Jean-Marc Astesana
4
Sebagai catatan tambahan, jika Anda perlu menggunakan hasil dari ekspresi ini di tempat lain dalam kode Anda, Anda dapat mengetikkan hasilnya menjadi ganda seperti: return (Double) engine.eval(foo);
Ben Visness
38
Catatan keamanan: Anda tidak boleh menggunakan ini dalam konteks server dengan input pengguna. JavaScript yang dieksekusi dapat mengakses semua kelas Java dan dengan demikian membajak aplikasi Anda tanpa batas.
Boann
3
@Boann, saya meminta Anda untuk memberi saya referensi tentang apa yang Anda katakan. (Pastinya 100%)
partho
17
@partho new javax.script.ScriptEngineManager().getEngineByName("JavaScript") .eval("var f = new java.io.FileWriter('hello.txt'); f.write('UNLIMITED POWER!'); f.close();");- akan menulis file melalui JavaScript ke (secara default) direktori saat ini program
Boann
236
Saya telah menulis evalmetode ini untuk ekspresi aritmatika untuk menjawab pertanyaan ini. Itu penambahan, pengurangan, perkalian, pembagian, eksponensial (menggunakan ^simbol), dan beberapa fungsi dasar seperti sqrt. Ini mendukung pengelompokan menggunakan (... ), dan itu membuat operator lebih diutamakan dan aturan associativity .
publicstaticdouble eval(finalString str){returnnewObject(){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();returntrue;}returnfalse;}double parse(){
nextChar();double x = parseExpression();if(pos < str.length())thrownewRuntimeException("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 `^` factordouble parseExpression(){double x = parseTerm();for(;;){if(eat('+')) x += parseTerm();// additionelseif(eat('-')) x -= parseTerm();// subtractionelsereturn x;}}double parseTerm(){double x = parseFactor();for(;;){if(eat('*')) x *= parseFactor();// multiplicationelseif(eat('/')) x /= parseFactor();// divisionelsereturn x;}}double parseFactor(){if(eat('+'))return parseFactor();// unary plusif(eat('-'))return-parseFactor();// unary minusdouble x;int startPos =this.pos;if(eat('(')){// parentheses
x = parseExpression();
eat(')');}elseif((ch >='0'&& ch <='9')|| ch =='.'){// numberswhile((ch >='0'&& ch <='9')|| ch =='.') nextChar();
x =Double.parseDouble(str.substring(startPos,this.pos));}elseif(ch >='a'&& ch <='z'){// functionswhile(ch >='a'&& ch <='z') nextChar();String func = str.substring(startPos,this.pos);
x = parseFactor();if(func.equals("sqrt")) x =Math.sqrt(x);elseif(func.equals("sin")) x =Math.sin(Math.toRadians(x));elseif(func.equals("cos")) x =Math.cos(Math.toRadians(x));elseif(func.equals("tan")) x =Math.tan(Math.toRadians(x));elsethrownewRuntimeException("Unknown function: "+ func);}else{thrownewRuntimeException("Unexpected: "+(char)ch);}if(eat('^')) x =Math.pow(x, parseFactor());// exponentiationreturn x;}}.parse();}
Parser adalah parser keturunan rekursif , sehingga secara internal menggunakan metode parse yang terpisah untuk setiap tingkat prioritas operator dalam tata bahasanya. Saya menyimpannya singkat sehingga mudah untuk dimodifikasi, tetapi berikut adalah beberapa ide yang mungkin ingin Anda kembangkan:
Variabel:
Bit parser yang membaca nama untuk fungsi dapat dengan mudah diubah untuk menangani variabel kustom juga, dengan mencari nama dalam tabel variabel yang diteruskan ke evalmetode, seperti a Map<String,Double> variables.
Kompilasi dan evaluasi terpisah:
Bagaimana jika, setelah menambahkan dukungan untuk variabel, Anda ingin mengevaluasi ekspresi yang sama jutaan kali dengan variabel yang berubah, tanpa menguraikannya setiap waktu? Itu mungkin. Pertama-tama tentukan antarmuka yang digunakan untuk mengevaluasi ekspresi yang dikompilasi:
Sekarang ubah semua metode yang mengembalikan doubles, jadi alih-alih mereka mengembalikan instance dari antarmuka itu. Sintaks lambda Java 8 sangat cocok untuk ini. Contoh salah satu metode yang diubah:
Expression parseExpression(){Expression x = parseTerm();for(;;){if(eat('+')){// additionExpression a = x, b = parseTerm();
x =(()-> a.eval()+ b.eval());}elseif(eat('-')){// subtractionExpression a = x, b = parseTerm();
x =(()-> a.eval()- b.eval());}else{return x;}}}
Itu membangun pohon Expressionobjek rekursif yang mewakili ekspresi dikompilasi ( pohon sintaksis abstrak ). Kemudian Anda dapat mengompilasinya sekali dan mengevaluasinya berulang kali dengan nilai yang berbeda:
publicstaticvoid main(String[] args){Map<String,Double> variables =newHashMap<>();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());}}
Tipe data yang berbeda:
Alih-alih double, Anda bisa mengubah evaluator untuk menggunakan sesuatu yang lebih kuat seperti BigDecimal, atau kelas yang mengimplementasikan bilangan kompleks, atau bilangan rasional (pecahan). Anda bahkan dapat menggunakan Object, memungkinkan beberapa campuran tipe data dalam ekspresi, seperti bahasa pemrograman nyata. :)
Semua kode dalam jawaban ini dirilis ke domain publik . Selamat bersenang-senang!
Algoritma yang bagus, mulai dari itu saya berhasil operator impresi dan logis. Kami membuat kelas terpisah untuk fungsi untuk mengevaluasi fungsi, jadi seperti ide Anda tentang variabel, saya membuat peta dengan fungsi dan menjaga nama fungsi. Setiap fungsi mengimplementasikan antarmuka dengan metode eval (T rightOperator, T leftOperator), jadi kapan saja kita dapat menambahkan fitur tanpa mengubah kode algoritma. Dan itu ide yang bagus untuk membuatnya bekerja dengan tipe generik. Terimakasih!
Vasile Bors
1
Bisakah Anda menjelaskan logika di balik algoritma ini?
iYonatan
1
Saya mencoba memberikan deskripsi tentang apa yang saya pahami dari kode yang ditulis oleh Boann, dan contoh-contoh yang dijelaskan wiki. Logika algoritma ini mulai dari aturan perintah operasi. 1. tanda operator | evaluasi variabel | panggilan fungsi | tanda kurung (sub-ekspresi); 2. eksponensial; 3. perkalian, pembagian; 4. penambahan, pengurangan;
Vasile Bors
1
Metode algoritma dibagi untuk setiap tingkat urutan operasi sebagai berikut: parseFactor = 1. tanda operator | evaluasi variabel | panggilan fungsi | tanda kurung (sub-ekspresi); 2. eksponensial; parseTerms = 3. perkalian, pembagian; parseExpression = 4. Selain itu, kurangi. Algoritme, memanggil metode dengan urutan terbalik (parseExpression -> parseTerms -> parseFactor -> parseExpression (untuk sub-ekspresi)), tetapi setiap metode pada baris pertama memanggil metode ke tingkat berikutnya, sehingga seluruh metode urutan eksekusi akan menjadi urutan operasi sebenarnya normal.
Vasile Bors
1
Misalnya metode parseExpression, double x = parseTerm(); evaluasi operator kiri, setelah for (;;) {...}evaluasi ini, operasi sukses tingkat pemesanan aktual (penambahan, pengurangan). Logika yang sama adalah dan dalam metode parseTerm. The parseFactor tidak memiliki level berikutnya, jadi hanya ada evaluasi metode / variabel atau dalam kasus paranthesis - evaluasi sub-ekspresi. The boolean eat(int charToEat)Metode pemeriksaan kesetaraan karakter kursor saat ini dengan karakter charToEat, jika pengembalian sama benar dan pindah kursor ke karakter berikutnya, saya menggunakan nama 'menerima' untuk itu.
Vasile Bors
34
Cara yang benar untuk menyelesaikan ini adalah dengan lexer dan parser . Anda dapat menulis sendiri versi sederhana ini, atau halaman tersebut juga memiliki tautan ke Java lexers dan parser.
Membuat parser keturunan rekursif adalah latihan pembelajaran yang sangat baik.
Untuk proyek universitas saya, saya mencari parser / evaluator yang mendukung rumus dasar dan persamaan yang lebih rumit (terutama operator yang diiterasi). Saya menemukan perpustakaan open source yang sangat bagus untuk JAVA dan .NET disebut mXparser. Saya akan memberikan beberapa contoh untuk membuat beberapa perasaan pada sintaks, untuk instruksi lebih lanjut silakan kunjungi situs web proyek (terutama bagian tutorial).
Expression e =newExpression("( 2 + 3/4 + sin(pi) )/2");double v = e.calculate()
2 - Argumen dan konstanta yang ditentukan pengguna
Argument x =newArgument("x = 10");Constant a =newConstant("a = pi^2");Expression e =newExpression("cos(a*x)", x, a);double v = e.calculate()
3 - Fungsi yang ditentukan pengguna
Function f =newFunction("f(x, y, z) = sin(x) + cos(y*z)");Expression e =newExpression("f(3,2,5)", f);double v = e.calculate()
4 - Iterasi
Expression e =newExpression("sum( i, 1, 100, sin(i) )");double v = e.calculate()
Ditemukan baru-baru - dalam kasus Anda ingin mencoba sintaks (dan melihat kasus penggunaan lanjutan) Anda dapat men-download Scalar Kalkulator aplikasi yang didukung oleh mXparser.
Sejauh ini adalah perpustakaan matematika terbaik di luar sana; sederhana untuk memulai, mudah digunakan dan dapat diperpanjang. Pasti harus menjadi jawaban teratas.
Saya menemukan mXparser tidak dapat mengidentifikasi formula ilegal, misalnya, '0/0' akan mendapatkan hasil sebagai '0'. Bagaimana saya bisa mengatasi masalah ini?
lulijun
Baru saja menemukan solusinya, expression.setSlientMode ()
lulijun
20
HERE adalah pustaka sumber terbuka lain di GitHub bernama EvalEx.
Berbeda dengan mesin JavaScript, perpustakaan ini hanya berfokus dalam mengevaluasi ekspresi matematika saja. Selain itu, perpustakaan dapat diperluas dan mendukung penggunaan operator boolean serta tanda kurung.
Ini tidak masalah, tetapi gagal ketika kami mencoba mengalikan nilai kelipatan 5 atau 10, misalnya 65 * 6 menghasilkan 3,9E + 2 ...
paarth batra
Tapi ada cara untuk memperbaikinya dengan melemparkannya ke int yaitu int output = (int) 65 * 6 sekarang akan menghasilkan 390
paarth batra
1
Untuk memperjelas, itu bukan masalah perpustakaan melainkan masalah dengan representasi angka sebagai nilai floating point.
DavidBittner
Perpustakaan ini sangat bagus. @paarth batra Casting ke int akan menghapus semua titik desimal. Gunakan ini sebagai gantinya: expression.eval (). ToPlainString ();
Anda dapat mengevaluasi ekspresi dengan mudah jika aplikasi Java Anda sudah mengakses database, tanpa menggunakan JAR lainnya.
Beberapa database mengharuskan Anda untuk menggunakan tabel dummy (misalnya, tabel "ganda" Oracle) dan yang lain akan memungkinkan Anda untuk mengevaluasi ekspresi tanpa "memilih" dari tabel mana pun.
Misalnya, di Sql Server atau Sqlite
select (((12.10+12.0))/233.0) amount
dan di Oracle
select (((12.10+12.0))/233.0) amount from dual;
Keuntungan menggunakan DB adalah Anda dapat mengevaluasi banyak ekspresi secara bersamaan. Juga sebagian besar DB akan memungkinkan Anda untuk menggunakan ekspresi yang sangat kompleks dan juga akan memiliki sejumlah fungsi tambahan yang dapat dipanggil seperlunya.
Namun kinerja dapat menderita jika banyak ekspresi tunggal perlu dievaluasi secara individual, terutama ketika DB terletak di server jaringan.
Berikut ini mengatasi masalah kinerja sampai batas tertentu, dengan menggunakan database di memori Sqlite.
Tergantung pada apa Anda menggunakan DB. Jika Anda ingin memastikan, Anda dapat dengan mudah membuat DB sqlite kosong, khusus untuk evaluasi matematika.
Mengizinkan skrip yang menyertakan referensi ke objek java.
// Create or retrieve a JexlEngineJexlEngine jexl =newJexlEngine();// Create an expression objectString jexlExp ="foo.innerFoo.bar()";Expression e = jexl.createExpression( jexlExp );// Create a context and add dataJexlContext jctx =newMapContext();
jctx.set("foo",newFoo());// Now evaluate the expression, getting the resultObject o = e.evaluate(jctx);
Harap rangkum informasi dari artikel, jika tautannya rusak.
DJClayworth
Saya telah memutakhirkan jawaban untuk menyertakan bit yang relevan dari artikel
Brad Parks
1
dalam praktiknya, JEXL lambat (menggunakan introspeksi kacang), memiliki masalah kinerja dengan multithreading (cache global)
Nishi
Senang tahu @Nishi! - Kasus penggunaan saya adalah untuk debugging hal-hal di lingkungan hidup, tetapi bukan bagian dari aplikasi yang digunakan normal.
Brad Parks
10
Cara lain adalah dengan menggunakan Spring Expression Language atau SpEL yang jauh lebih banyak bersama dengan mengevaluasi ekspresi matematika karena itu mungkin sedikit berlebihan. Anda tidak harus menggunakan kerangka kerja Spring untuk menggunakan pustaka ekspresi ini karena itu berdiri sendiri. Menyalin contoh dari dokumentasi SpEL:
jika kita akan mengimplementasikannya maka kita dapat menggunakan algoritma di bawah ini: -
Meskipun masih ada token yang harus dibaca,
1.1 Dapatkan token berikutnya. 1.2 Jika tokennya adalah:
1.2.1 Nomor: dorong ke tumpukan nilai.
1.2.2 Variabel: dapatkan nilainya, dan dorong ke tumpukan nilai.
1.2.3 Tanda kurung kiri: dorong ke tumpukan operator.
1.2.4 Tanda kurung yang tepat:
1While the thing on top of the operator stack is not a
left parenthesis,1Pop the operator from the operator stack.2Pop the value stack twice, getting two operands.3Apply the operator to the operands, in the correct order.4Push the result onto the value stack.2Pop the left parenthesis from the operator stack, and discard it.
1.2.5 Seorang operator (sebut saja iniOp):
1While the operator stack is not empty, and the top thing on the
operator stack has the same or greater precedence as thisOp,1Pop the operator from the operator stack.2Pop the value stack twice, getting two operands.3Apply the operator to the operands, in the correct order.4Push the result onto the value stack.2Push thisOp onto the operator stack.
Meskipun tumpukan operator tidak kosong, 1 Angkat operator dari tumpukan operator. 2 Pop nilai stack dua kali, dapatkan dua operan. 3 Terapkan operator ke operan, dalam urutan yang benar. 4 Dorong hasilnya ke tumpukan nilai.
Pada titik ini tumpukan operator harus kosong, dan tumpukan nilai harus hanya memiliki satu nilai di dalamnya, yang merupakan hasil akhir.
Saya pikir apa pun cara Anda melakukan ini akan melibatkan banyak pernyataan bersyarat. Tetapi untuk operasi tunggal seperti dalam contoh Anda, Anda dapat membatasi hingga 4 jika pernyataan dengan sesuatu seperti
String math ="1+4";if(math.split("+").length ==2){//do calculation}elseif(math.split("-").length ==2){//do calculation}...
Itu menjadi jauh lebih rumit ketika Anda ingin berurusan dengan beberapa operasi seperti "4 + 5 * 6".
Jika Anda mencoba membangun kalkulator maka saya lebih baik melewati setiap bagian perhitungan secara terpisah (setiap angka atau operator) daripada sebagai string tunggal.
Ini menjadi jauh lebih rumit segera setelah Anda harus berurusan dengan beberapa operasi, prioritas operator, tanda kurung, ... pada kenyataannya apa pun yang menjadi ciri ekspresi aritmatika nyata. Anda tidak bisa sampai di sana mulai dari teknik ini.
Marquis of Lorne
4
Sudah terlambat untuk menjawab tetapi saya menemukan situasi yang sama untuk mengevaluasi ekspresi di java, mungkin membantu seseorang
MVELtidak evaluasi runtime ekspresi, kita bisa menulis kode java Stringuntuk mendapatkannya dievaluasi dalam hal ini.
String expressionStr ="x+y";Map<String,Object> vars =newHashMap<String,Object>();
vars.put("x",10);
vars.put("y",20);ExecutableStatement statement =(ExecutableStatement) MVEL.compileExpression(expressionStr);Object result = MVEL.executeExpression(statement, vars);
ExprEvaluator util =newExprEvaluator();IExpr result = util.evaluate("10-40");System.out.println(result.toString());// -> "-30"
Perhatikan bahwa ekspresi yang pasti lebih kompleks dapat dievaluasi:
// 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
Ini sebenarnya melengkapi jawaban yang diberikan oleh @Boann. Ini memiliki sedikit bug yang menyebabkan "-2 ^ 2" memberikan hasil yang salah dari -4.0. Masalah untuk itu adalah titik di mana eksponensial dievaluasi dalam bukunya. Pindahkan eksponensial ke blok parseTerm (), dan Anda akan baik-baik saja. Lihat di bawah ini, yang merupakan jawaban @ Boann sedikit dimodifikasi. Modifikasi ada di komentar.
publicstaticdouble eval(finalString str){returnnewObject(){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();returntrue;}returnfalse;}double parse(){
nextChar();double x = parseExpression();if(pos < str.length())thrownewRuntimeException("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 `^` factordouble parseExpression(){double x = parseTerm();for(;;){if(eat('+')) x += parseTerm();// additionelseif(eat('-')) x -= parseTerm();// subtractionelsereturn x;}}double parseTerm(){double x = parseFactor();for(;;){if(eat('*')) x *= parseFactor();// multiplicationelseif(eat('/')) x /= parseFactor();// divisionelseif(eat('^')) x =Math.pow(x, parseFactor());//exponentiation -> Moved in to here. So the problem is fixedelsereturn x;}}double parseFactor(){if(eat('+'))return parseFactor();// unary plusif(eat('-'))return-parseFactor();// unary minusdouble x;int startPos =this.pos;if(eat('(')){// parentheses
x = parseExpression();
eat(')');}elseif((ch >='0'&& ch <='9')|| ch =='.'){// numberswhile((ch >='0'&& ch <='9')|| ch =='.') nextChar();
x =Double.parseDouble(str.substring(startPos,this.pos));}elseif(ch >='a'&& ch <='z'){// functionswhile(ch >='a'&& ch <='z') nextChar();String func = str.substring(startPos,this.pos);
x = parseFactor();if(func.equals("sqrt")) x =Math.sqrt(x);elseif(func.equals("sin")) x =Math.sin(Math.toRadians(x));elseif(func.equals("cos")) x =Math.cos(Math.toRadians(x));elseif(func.equals("tan")) x =Math.tan(Math.toRadians(x));elsethrownewRuntimeException("Unknown function: "+ func);}else{thrownewRuntimeException("Unexpected: "+(char)ch);}//if (eat('^')) x = Math.pow(x, parseFactor()); // exponentiation -> This is causing a bit of problemreturn x;}}.parse();}
-2^2 = -4sebenarnya normal, dan bukan bug. Itu akan dikelompokkan seperti -(2^2). Cobalah di Desmos, misalnya. Kode Anda sebenarnya memperkenalkan beberapa bug. Yang pertama adalah bahwa ^tidak ada lagi grup yang kanan-ke-kiri. Dengan kata lain, 2^3^2seharusnya dikelompokkan sebagai suka 2^(3^2)karena ^asosiatif yang benar, tetapi modifikasi Anda membuatnya suka (2^3)^2. Yang kedua adalah yang ^seharusnya memiliki prioritas lebih tinggi daripada *dan /, tetapi modifikasi Anda memperlakukannya sama. Lihat ideone.com/iN2mMa .
Radiodef
Jadi, apa yang Anda sarankan adalah bahwa eksponensial lebih baik disimpan di tempat yang semula bukan?
Romeo Sierra
Ya, itulah yang saya sarankan.
Radiodef
4
packageExpressionCalculator.expressioncalculator;import java.text.DecimalFormat;import java.util.Scanner;publicclassExpressionCalculator{privatestaticString 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;}publicstaticDouble evaluate(String expr){DecimalFormat df =newDecimalFormat("#.####");//Format the expression properly before performing operationsString expression = addSpaces(expr);try{//We will evaluate using rule BDMAS, i.e. brackets, division, power, multiplication, addition and//subtraction will be processed in following orderint 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 ="/";}elseif(expression.indexOf(" ^ ")!=-1){
operation ="^";}elseif(expression.indexOf(" * ")!=-1){
operation ="*";}elseif(expression.indexOf(" + ")!=-1){
operation ="+";}elseif(expression.indexOf(" - ")!=-1){//Avoid negative numbers
operation ="-";}else{returnDouble.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){returnnull;}
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);}returnDouble.valueOf(df.format(evaluate(expression.trim())));}}}catch(Exception exp){
exp.printStackTrace();}return0.0;}publicstaticvoid main(String args[]){Scanner scanner =newScanner(System.in);System.out.print("Enter an Mathematical Expression to Evaluate: ");String input = scanner.nextLine();System.out.println(evaluate(input));}
Anda harus membaca tentang cara menulis parser ekspresi matematika yang efisien. Ada metodologi ilmu komputer untuk itu. Lihatlah ANTLR, misalnya. Jika Anda berpikir dengan baik tentang apa yang Anda tulis, Anda akan melihat bahwa hal-hal seperti (a + b / -c) * (e / f) tidak akan berfungsi dengan ide Anda atau kodenya akan menjadi super duper kotor dan tidak efisien.
Daniel Nuriyev
2
Dimungkinkan untuk mengubah string ekspresi apa pun dalam notasi infiks ke notasi postfix menggunakan algoritma shunting-yard Djikstra . Hasil algoritma kemudian dapat berfungsi sebagai input ke algoritma postfix dengan mengembalikan hasil dari ekspresi.
Tidak menangani prioritas operator dengan benar. Ada cara standar untuk melakukan ini, dan ini bukan salah satunya.
Marquis of Lorne
EJP, bisa tolong tunjukkan di mana ada masalah dengan prioritas operator? Saya sepenuhnya setuju pada kenyataan bahwa itu bukan cara standar untuk melakukannya. cara standar sudah disebutkan di posting sebelumnya, idenya adalah untuk menunjukkan cara lain untuk melakukannya.
Efi G
2
Pustaka eksternal seperti RHINO atau NASHORN dapat digunakan untuk menjalankan javascript. Dan javascript dapat mengevaluasi formula sederhana tanpa mem-paring string. Tidak ada dampak kinerja juga jika kode ditulis dengan baik. Di bawah ini adalah contoh dengan RHINO -
Jawaban:
Dengan JDK1.6, Anda dapat menggunakan mesin Javascript bawaan.
sumber
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();");
- akan menulis file melalui JavaScript ke (secara default) direktori saat ini programSaya telah menulis
eval
metode ini untuk ekspresi aritmatika untuk menjawab pertanyaan ini. Itu penambahan, pengurangan, perkalian, pembagian, eksponensial (menggunakan^
simbol), dan beberapa fungsi dasar sepertisqrt
. Ini mendukung pengelompokan menggunakan(
...)
, dan itu membuat operator lebih diutamakan dan aturan associativity .Contoh:
Output: 7.5 (yang benar)
Parser adalah parser keturunan rekursif , sehingga secara internal menggunakan metode parse yang terpisah untuk setiap tingkat prioritas operator dalam tata bahasanya. Saya menyimpannya singkat sehingga mudah untuk dimodifikasi, tetapi berikut adalah beberapa ide yang mungkin ingin Anda kembangkan:
Variabel:
Bit parser yang membaca nama untuk fungsi dapat dengan mudah diubah untuk menangani variabel kustom juga, dengan mencari nama dalam tabel variabel yang diteruskan ke
eval
metode, seperti aMap<String,Double> variables
.Kompilasi dan evaluasi terpisah:
Bagaimana jika, setelah menambahkan dukungan untuk variabel, Anda ingin mengevaluasi ekspresi yang sama jutaan kali dengan variabel yang berubah, tanpa menguraikannya setiap waktu? Itu mungkin. Pertama-tama tentukan antarmuka yang digunakan untuk mengevaluasi ekspresi yang dikompilasi:
Sekarang ubah semua metode yang mengembalikan
double
s, jadi alih-alih mereka mengembalikan instance dari antarmuka itu. Sintaks lambda Java 8 sangat cocok untuk ini. Contoh salah satu metode yang diubah:Itu membangun pohon
Expression
objek rekursif yang mewakili ekspresi dikompilasi ( pohon sintaksis abstrak ). Kemudian Anda dapat mengompilasinya sekali dan mengevaluasinya berulang kali dengan nilai yang berbeda:Tipe data yang berbeda:
Alih-alih
double
, Anda bisa mengubah evaluator untuk menggunakan sesuatu yang lebih kuat sepertiBigDecimal
, atau kelas yang mengimplementasikan bilangan kompleks, atau bilangan rasional (pecahan). Anda bahkan dapat menggunakanObject
, memungkinkan beberapa campuran tipe data dalam ekspresi, seperti bahasa pemrograman nyata. :)Semua kode dalam jawaban ini dirilis ke domain publik . Selamat bersenang-senang!
sumber
double x = parseTerm();
evaluasi operator kiri, setelahfor (;;) {...}
evaluasi ini, operasi sukses tingkat pemesanan aktual (penambahan, pengurangan). Logika yang sama adalah dan dalam metode parseTerm. The parseFactor tidak memiliki level berikutnya, jadi hanya ada evaluasi metode / variabel atau dalam kasus paranthesis - evaluasi sub-ekspresi. Theboolean eat(int charToEat)
Metode pemeriksaan kesetaraan karakter kursor saat ini dengan karakter charToEat, jika pengembalian sama benar dan pindah kursor ke karakter berikutnya, saya menggunakan nama 'menerima' untuk itu.Cara yang benar untuk menyelesaikan ini adalah dengan lexer dan parser . Anda dapat menulis sendiri versi sederhana ini, atau halaman tersebut juga memiliki tautan ke Java lexers dan parser.
Membuat parser keturunan rekursif adalah latihan pembelajaran yang sangat baik.
sumber
Untuk proyek universitas saya, saya mencari parser / evaluator yang mendukung rumus dasar dan persamaan yang lebih rumit (terutama operator yang diiterasi). Saya menemukan perpustakaan open source yang sangat bagus untuk JAVA dan .NET disebut mXparser. Saya akan memberikan beberapa contoh untuk membuat beberapa perasaan pada sintaks, untuk instruksi lebih lanjut silakan kunjungi situs web proyek (terutama bagian tutorial).
https://mathparser.org/
https://mathparser.org/mxparser-tutorial/
https://mathparser.org/api/
Dan beberapa contoh
1 - Furmula sederhana
2 - Argumen dan konstanta yang ditentukan pengguna
3 - Fungsi yang ditentukan pengguna
4 - Iterasi
Ditemukan baru-baru - dalam kasus Anda ingin mencoba sintaks (dan melihat kasus penggunaan lanjutan) Anda dapat men-download Scalar Kalkulator aplikasi yang didukung oleh mXparser.
salam Hormat
sumber
HERE adalah pustaka sumber terbuka lain di GitHub bernama EvalEx.
Berbeda dengan mesin JavaScript, perpustakaan ini hanya berfokus dalam mengevaluasi ekspresi matematika saja. Selain itu, perpustakaan dapat diperluas dan mendukung penggunaan operator boolean serta tanda kurung.
sumber
Anda juga dapat mencoba juru bahasa BeanShell :
sumber
Anda dapat mengevaluasi ekspresi dengan mudah jika aplikasi Java Anda sudah mengakses database, tanpa menggunakan JAR lainnya.
Beberapa database mengharuskan Anda untuk menggunakan tabel dummy (misalnya, tabel "ganda" Oracle) dan yang lain akan memungkinkan Anda untuk mengevaluasi ekspresi tanpa "memilih" dari tabel mana pun.
Misalnya, di Sql Server atau Sqlite
dan di Oracle
Keuntungan menggunakan DB adalah Anda dapat mengevaluasi banyak ekspresi secara bersamaan. Juga sebagian besar DB akan memungkinkan Anda untuk menggunakan ekspresi yang sangat kompleks dan juga akan memiliki sejumlah fungsi tambahan yang dapat dipanggil seperlunya.
Namun kinerja dapat menderita jika banyak ekspresi tunggal perlu dievaluasi secara individual, terutama ketika DB terletak di server jaringan.
Berikut ini mengatasi masalah kinerja sampai batas tertentu, dengan menggunakan database di memori Sqlite.
Berikut ini contoh kerja lengkap di Jawa
Tentu saja Anda dapat memperluas kode di atas untuk menangani banyak perhitungan secara bersamaan.
sumber
Artikel ini membahas berbagai pendekatan. Berikut adalah 2 pendekatan utama yang disebutkan dalam artikel:
JEXL dari Apache
Mengizinkan skrip yang menyertakan referensi ke objek java.
Gunakan mesin javascript yang tertanam di JDK:
sumber
Cara lain adalah dengan menggunakan Spring Expression Language atau SpEL yang jauh lebih banyak bersama dengan mengevaluasi ekspresi matematika karena itu mungkin sedikit berlebihan. Anda tidak harus menggunakan kerangka kerja Spring untuk menggunakan pustaka ekspresi ini karena itu berdiri sendiri. Menyalin contoh dari dokumentasi SpEL:
Baca contoh SpEL lebih ringkas di sini dan dokumen selengkapnya di sini
sumber
jika kita akan mengimplementasikannya maka kita dapat menggunakan algoritma di bawah ini: -
Meskipun masih ada token yang harus dibaca,
1.1 Dapatkan token berikutnya. 1.2 Jika tokennya adalah:
1.2.1 Nomor: dorong ke tumpukan nilai.
1.2.2 Variabel: dapatkan nilainya, dan dorong ke tumpukan nilai.
1.2.3 Tanda kurung kiri: dorong ke tumpukan operator.
1.2.4 Tanda kurung yang tepat:
1.2.5 Seorang operator (sebut saja iniOp):
Meskipun tumpukan operator tidak kosong, 1 Angkat operator dari tumpukan operator. 2 Pop nilai stack dua kali, dapatkan dua operan. 3 Terapkan operator ke operan, dalam urutan yang benar. 4 Dorong hasilnya ke tumpukan nilai.
Pada titik ini tumpukan operator harus kosong, dan tumpukan nilai harus hanya memiliki satu nilai di dalamnya, yang merupakan hasil akhir.
sumber
Ini adalah alternatif lain yang menarik https://github.com/Shy-Ta/expression-evaluator-demo
Penggunaannya sangat sederhana dan menyelesaikan pekerjaan, misalnya:
sumber
Sepertinya JEP harus melakukan pekerjaan itu
sumber
Saya pikir apa pun cara Anda melakukan ini akan melibatkan banyak pernyataan bersyarat. Tetapi untuk operasi tunggal seperti dalam contoh Anda, Anda dapat membatasi hingga 4 jika pernyataan dengan sesuatu seperti
Itu menjadi jauh lebih rumit ketika Anda ingin berurusan dengan beberapa operasi seperti "4 + 5 * 6".
Jika Anda mencoba membangun kalkulator maka saya lebih baik melewati setiap bagian perhitungan secara terpisah (setiap angka atau operator) daripada sebagai string tunggal.
sumber
Sudah terlambat untuk menjawab tetapi saya menemukan situasi yang sama untuk mengevaluasi ekspresi di java, mungkin membantu seseorang
MVEL
tidak evaluasi runtime ekspresi, kita bisa menulis kode javaString
untuk mendapatkannya dievaluasi dalam hal ini.sumber
Anda mungkin melihat kerangka kerja Symja :
Perhatikan bahwa ekspresi yang pasti lebih kompleks dapat dievaluasi:
sumber
Coba kode contoh berikut menggunakan mesin Javascript JDK1.6 dengan penanganan injeksi kode.
sumber
Ini sebenarnya melengkapi jawaban yang diberikan oleh @Boann. Ini memiliki sedikit bug yang menyebabkan "-2 ^ 2" memberikan hasil yang salah dari -4.0. Masalah untuk itu adalah titik di mana eksponensial dievaluasi dalam bukunya. Pindahkan eksponensial ke blok parseTerm (), dan Anda akan baik-baik saja. Lihat di bawah ini, yang merupakan jawaban @ Boann sedikit dimodifikasi. Modifikasi ada di komentar.
sumber
-2^2 = -4
sebenarnya normal, dan bukan bug. Itu akan dikelompokkan seperti-(2^2)
. Cobalah di Desmos, misalnya. Kode Anda sebenarnya memperkenalkan beberapa bug. Yang pertama adalah bahwa^
tidak ada lagi grup yang kanan-ke-kiri. Dengan kata lain,2^3^2
seharusnya dikelompokkan sebagai suka2^(3^2)
karena^
asosiatif yang benar, tetapi modifikasi Anda membuatnya suka(2^3)^2
. Yang kedua adalah yang^
seharusnya memiliki prioritas lebih tinggi daripada*
dan/
, tetapi modifikasi Anda memperlakukannya sama. Lihat ideone.com/iN2mMa .}
sumber
Bagaimana dengan sesuatu yang seperti ini:
dan melakukan hal yang sama untuk setiap operator matematika lainnya sesuai ..
sumber
Dimungkinkan untuk mengubah string ekspresi apa pun dalam notasi infiks ke notasi postfix menggunakan algoritma shunting-yard Djikstra . Hasil algoritma kemudian dapat berfungsi sebagai input ke algoritma postfix dengan mengembalikan hasil dari ekspresi.
Saya menulis artikel tentang itu di sini, dengan implementasi di java
sumber
Namun pilihan lain: https://github.com/stefanhaustein/expressionparser
Saya telah menerapkan ini untuk memiliki opsi yang sederhana namun fleksibel untuk mengizinkan keduanya:
TreeBuilder yang ditautkan di atas adalah bagian dari paket demo CAS yang melakukan derivasi simbolik. Ada juga contoh interpreter BASIC dan saya sudah mulai membangun juru bahasa TypeScript yang menggunakannya.
sumber
Kelas Java yang dapat mengevaluasi ekspresi matematika:
sumber
Pustaka eksternal seperti RHINO atau NASHORN dapat digunakan untuk menjalankan javascript. Dan javascript dapat mengevaluasi formula sederhana tanpa mem-paring string. Tidak ada dampak kinerja juga jika kode ditulis dengan baik. Di bawah ini adalah contoh dengan RHINO -
sumber
sumber