Mam ciąg tak:Czy istnieje funkcja eval() w Javie?
String str = "4*5";
teraz muszę uzyskać wynik 20
za pomocą łańcucha.
Wiem, że w niektórych innych językach funkcja eval()
to zrobi. Jak mogę to zrobić w Javie?
Mam ciąg tak:Czy istnieje funkcja eval() w Javie?
String str = "4*5";
teraz muszę uzyskać wynik 20
za pomocą łańcucha.
Wiem, że w niektórych innych językach funkcja eval()
to zrobi. Jak mogę to zrobić w Javie?
Możesz użyć klasy ScriptEngine
i ocenić ją jako ciąg znaków JavaScript.
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
Object result = engine.eval("4*5");
Może być lepszy sposób, ale ten działa.
Załadowanie całego interpretera JavaScript, aby zrobić jakąś matematykę, wydaje się ogromną stratą. Ale masz rację, działa, zwłaszcza jeśli wydajność nie jest priorytetem. –
Oprócz tego, że jest trochę przesady, za pomocą interpretera JavaScript otwiera się do wstrzyknięcia kodu. Jeśli nie będziesz ściśle kontrolować wyrażenia, ktoś może wysłać ci 'while (true) {3 + 4;}' i zawiesić maszynę JVM. – Thilo
Jest wydajny pod względem czasu kodowania. Jeśli używasz go przez cały czas, możesz rozważyć przechowywanie go w pamięci, być może w obiekcie singleton. –
Nie, nie można mieć ogólnej "eval" w Javie (lub dowolnym języku kompilowanym). Chyba, że chcesz napisać kompilator Javy ORAZ maszynę JVM, która zostanie wykonana wewnątrz twojego programu Java.
Tak, możesz mieć bibliotekę do oceny numerycznych wyrażeń algebraicznych, takich jak powyższy - see this thread for discussion.
Nie ma standardowej klasy ani metody Java, która zrobi to, co chcesz. Twoje opcje obejmują:
Wybierz i użyj jakiejś zewnętrznej biblioteki oceny wyrażeń. Na przykład: JEL lub dowolna z pół tuzina bibliotek wymienionych na liście here.
Zawiń wyrażenie w kodzie źródłowym Java dla klasy za pomocą metody eval
, wyślij to do kompilatora Java, a następnie załaduj wynikową skompilowaną klasę.
Użyj języka skryptowego, który może być wywoływany z Javy jako ewaluator wyrażeń. Możliwości obejmują JavaScript, BeanShell i tak dalej.
Napisz swój własny ewaluator ekspresji od podstaw.
Pierwsze podejście jest prawdopodobnie najprostsze. Drugie i trzecie podejście jest potencjalnym zagrożeniem bezpieczeństwa, jeśli otrzymasz wyrażenie do oceny od niezaufanego użytkownika. (Zastanów się nad wprowadzeniem kodu).
Nie ma nic, co zrobi to w JavaSE; musielibyście znaleźć biblioteki innych firm lub napisać własne.
To zła odpowiedź! – user2284570
Wcześniejsze odpowiedzi nie zawierają standardowego interfejsu API w języku Java.
Możesz dodawać groovy pliki jar do swojej ścieżki, a groovy.util.Eval.me ("4 * 5") wykonuje twoją pracę.
Pisanie własnej biblioteki nie jest trudne, jak mogłoby się wydawać. Oto link do Shunting-yard algorithm z wyjaśnieniem algorytmu krok po kroku. Chociaż musisz najpierw przeanalizować dane wejściowe dla znaczników.
Są 2 inne pytania wich można podać kilka informacji TOO: Turn a String into a Math Expression? What's a good library for parsing mathematical expressions in java?
Istnieje bardzo niewiele przypadków rzeczywistego użytkowania, w którym jest w stanie ocenić String
jako fragment kodu Javy jest konieczne lub pożądane . To znaczy, pytając, jak to zrobić, to naprawdę jest XY problem: faktycznie masz inny problem, który można rozwiązać w inny sposób.
Najpierw zadaj sobie pytanie: skąd ta String
, którą chcesz ocenić, pochodzi? Czy inna część twojego programu ją wygenerowała, czy też była to informacja dostarczona przez użytkownika?
Kolejna część mojego programu generowane go: tak, chcesz jedną część swojego programu do decydowania o rodzaju pracy do wykonania, ale nie wykonać operację, a druga część, która wykonuje wybraną operację . Zamiast generować, a następnie oceniać model String
, należy użyć wzorca projektowego, odpowiednio do konkretnego przypadku.
To wejściowe użytkownika: użytkownik może wejściowe coś, w tym poleceń, które, gdy są wykonywane, mogą spowodować program do misbehave, crash, ujawnienia informacji, które powinny być tajne, uszkodzenie trwałe informacje (takie jak zawartość bazy danych) i inne takie złośliwości. Jedynym sposobem, aby temu zapobiec, byłoby przeanalizowanie samego siebie, sprawdź, czy nie było złośliwe, a następnie je przetestuj. Ale przeanalizowanie go samemu jest znaczną częścią pracy, którą wykonywałaby żądana funkcja eval
, więc nie oszczędziłeś sobie niczego. Co gorsza, sprawdzenie, czy arbitralny Java
nie był złośliwy, jest niemożliwe niemożliwe, ponieważ sprawdzanie, czy jest to halting problem.
To wejście użytkownika, ale składnia i semantyka dozwolonym tekście oceny jest znacznie ograniczona: Żaden obiekt ogólnego przeznaczenia można łatwo wdrożyć parser ogólnego przeznaczenia i oceniająca niezależnie ograniczone składnię i semantykę, które wybrałeś. To, co musisz zrobić, to zaimplementować analizator składni i oceniający dla wybranej składni i semantyki. Jeśli zadanie jest proste, można ręcznie napisać prosty recursive-descent lub parser skończony-maszyna. Jeśli zadanie jest trudne, możesz użyć narzędzia compiler-compiler (takiego jak ANTLR), aby wykonać część pracy za Ciebie.
Po prostu chcę zaimplementować kalkulator biurkowy!: Zadanie domowe, co? Gdybyś mógł zaimplementować ocenę wyrażenia wejściowego za pomocą dostarczonej funkcji eval
, to nie byłoby to zbyt trudne zadanie domowe, prawda? Twój program będzie miał trzy linie. Twój instruktor prawdopodobnie oczekuje, że napiszesz kod dla prostego parsera/oceniającego arytmetycznego. Jest dobrze znany algorytm, shunting-yard, który może okazać się przydatny.
Mogę doradzić, aby użyć Exp4j. Łatwo jest zrozumieć, jak można zobaczyć na poniższym przykładzie kodu:
Expression e = new ExpressionBuilder("3 * sin(y) - 2/(x - 2)")
.variables("x", "y")
.build()
.setVariable("x", 2.3)
.setVariable("y", 3.14);
double result = e.evaluate();
zabawy sposobem rozwiązania problemu może być kodowanie eval() funkcji na własną rękę! Zrobiłem to za Ciebie!
Można użyć biblioteki FunctionSolver prostu wpisując FunctionSolver.solveByX (funkcję, wartość) wewnątrz kodu. Atrybut jest ciągiem, który reprezentuje funkcję, którą chcesz rozwiązać, atrybut wartość jest wartością niezależnej zmiennej twojej funkcji (która MUSI być x).
Jeśli chcesz rozwiązać funkcję, która zawiera więcej niż jedną zmienną niezależną, można użyć FunctionSolver.solve (funkcję, wartości) gdzie wartości atrybut jest HashMap(String,Double) który zawiera wszystkie niezależne atrybuty (jako ciągi) i ich odpowiednie wartości (jako duble).
inny kawałek informacji: mam zakodowane prosty wersję FunctionSolver, więc jej obsługuje tylko Math methods które zwracają podwójną wartość, a która przyjmuje jedną lub dwie wartości pełnić pola (wystarczy użyć FunctionSolver.usableMathMethods() jeśli jesteś ciekawy) (Te metody to: bs, sin, cos, tan, atan2, sqrt, log, log10, pow, exp, min, max, copySign, signum, IEEEremainder, acos, asin, atan, cbrt , ceil, cosh, expm1, floor, hypot, log1p, nextAfter, nextDown, nextUp, random, rint, sinh, tanh, toDegrees, toRadians, ulp). Biblioteka ta obsługuje również następujące operatory: */+ -^(nawet jeśli java zwykle nie obsługuje operatora ^).
Ostatnia rzecz: podczas tworzenia tej biblioteki musiałem użyć reflections, aby zadzwonić pod numer Math methods. Myślę, że to naprawdę fajne, tylko have a look at this, jeśli jesteś zainteresowany!
To wszystko tutaj jest kod (a library):
package core;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
public abstract class FunctionSolver {
public static double solveNumericExpression (String expression) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return solve(expression, new HashMap<>());
}
public static double solveByX (String function, double value) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
HashMap<String, Double> values = new HashMap<>();
values.put("x", value);
return solveComplexFunction(function, function, values);
}
public static double solve (String function, HashMap<String,Double> values) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return solveComplexFunction(function, function, values);
}
private static double solveComplexFunction (String function, String motherFunction, HashMap<String, Double> values) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
int position = 0;
while(position < function.length()) {
if (alphabetic.contains(""+function.charAt(position))) {
if (position == 0 || !alphabetic.contains(""+function.charAt(position-1))) {
int endIndex = -1;
for (int j = position ; j < function.length()-1 ; j++) {
if (alphabetic.contains(""+function.charAt(j))
&& !alphabetic.contains(""+function.charAt(j+1))) {
endIndex = j;
break;
}
}
if (endIndex == -1 & alphabetic.contains(""+function.charAt(function.length()-1))) {
endIndex = function.length()-1;
}
if (endIndex != -1) {
String alphabeticElement = function.substring(position, endIndex+1);
if (Arrays.asList(usableMathMethods()).contains(alphabeticElement)) {
//Start analyzing a Math function
int closeParenthesisIndex = -1;
int openedParenthesisquantity = 0;
int commaIndex = -1;
for (int j = endIndex+1 ; j < function.length() ; j++) {
if (function.substring(j,j+1).equals("(")) {
openedParenthesisquantity++;
}else if (function.substring(j,j+1).equals(")")) {
openedParenthesisquantity--;
if (openedParenthesisquantity == 0) {
closeParenthesisIndex = j;
break;
}
}else if (function.substring(j,j+1).equals(",") & openedParenthesisquantity == 0) {
if (commaIndex == -1) {
commaIndex = j;
}else{
throw new IllegalArgumentException("The argument of math function (which is "+alphabeticElement+") has too many commas");
}
}
}
if (closeParenthesisIndex == -1) {
throw new IllegalArgumentException("The argument of a Math function (which is "+alphabeticElement+") hasn't got the closing bracket)");
}
String functionArgument = function.substring(endIndex+2,closeParenthesisIndex);
if (commaIndex != -1) {
double firstParameter = solveComplexFunction(functionArgument.substring(0,commaIndex),motherFunction,values);
double secondParameter = solveComplexFunction(functionArgument.substring(commaIndex+1),motherFunction,values);
Method mathMethod = Math.class.getDeclaredMethod(alphabeticElement, new Class<?>[] {double.class, double.class});
mathMethod.setAccessible(true);
String newKey = getNewKey(values);
values.put(newKey, (Double) mathMethod.invoke(null, firstParameter, secondParameter));
function = function.substring(0, position)+newKey
+((closeParenthesisIndex == function.length()-1)?(""):(function.substring(closeParenthesisIndex+1)));
}else {
double firstParameter = solveComplexFunction(functionArgument, motherFunction, values);
Method mathMethod = Math.class.getDeclaredMethod(alphabeticElement, new Class<?>[] {double.class});
mathMethod.setAccessible(true);
String newKey = getNewKey(values);
values.put(newKey, (Double) mathMethod.invoke(null, firstParameter));
function = function.substring(0, position)+newKey
+((closeParenthesisIndex == function.length()-1)?(""):(function.substring(closeParenthesisIndex+1)));
}
}else if (!values.containsKey(alphabeticElement)) {
throw new IllegalArgumentException("Found a group of letters ("+alphabeticElement+") which is neither a variable nor a Math function: ");
}
}
}
}
position++;
}
return solveBracketsFunction(function,motherFunction,values);
}
private static double solveBracketsFunction (String function,String motherFunction,HashMap<String, Double> values) throws IllegalArgumentException{
function = function.replace(" ", "");
String openingBrackets = "([{";
String closingBrackets = ")]}";
int parenthesisIndex = 0;
do {
int position = 0;
int openParenthesisBlockIndex = -1;
String currentOpeningBracket = openingBrackets.charAt(parenthesisIndex)+"";
String currentClosingBracket = closingBrackets.charAt(parenthesisIndex)+"";
if (contOccouranceIn(currentOpeningBracket,function) != contOccouranceIn(currentClosingBracket,function)) {
throw new IllegalArgumentException("Error: brackets are misused in the function "+function);
}
while (position < function.length()) {
if (function.substring(position,position+1).equals(currentOpeningBracket)) {
if (position != 0 && !operators.contains(function.substring(position-1,position))) {
throw new IllegalArgumentException("Error in function: there must be an operator following a "+currentClosingBracket+" breacket");
}
openParenthesisBlockIndex = position;
}else if (function.substring(position,position+1).equals(currentClosingBracket)) {
if (position != function.length()-1 && !operators.contains(function.substring(position+1,position+2))) {
throw new IllegalArgumentException("Error in function: there must be an operator before a "+currentClosingBracket+" breacket");
}
String newKey = getNewKey(values);
values.put(newKey, solveBracketsFunction(function.substring(openParenthesisBlockIndex+1,position),motherFunction, values));
function = function.substring(0,openParenthesisBlockIndex)+newKey
+((position == function.length()-1)?(""):(function.substring(position+1)));
position = -1;
}
position++;
}
parenthesisIndex++;
}while (parenthesisIndex < openingBrackets.length());
return solveBasicFunction(function,motherFunction, values);
}
private static double solveBasicFunction (String function, String motherFunction, HashMap<String, Double> values) throws IllegalArgumentException{
if (!firstContainsOnlySecond(function, alphanumeric+operators)) {
throw new IllegalArgumentException("The function "+function+" is not a basic function");
}
if (function.contains("**") |
function.contains("//") |
function.contains("--") |
function.contains("+*") |
function.contains("+/") |
function.contains("-*") |
function.contains("-/")) {
/*
* (-+ , +- , *- , *+ , /- , /+)> Those values are admitted
*/
throw new IllegalArgumentException("Operators are misused in the function");
}
function = function.replace(" ", "");
int position;
int operatorIndex = 0;
String currentOperator;
do {
currentOperator = operators.substring(operatorIndex,operatorIndex+1);
if (currentOperator.equals("*")) {
currentOperator+="/";
operatorIndex++;
}else if (currentOperator.equals("+")) {
currentOperator+="-";
operatorIndex++;
}
operatorIndex++;
position = 0;
while (position < function.length()) {
if ((position == 0 && !(""+function.charAt(position)).equals("-") && !(""+function.charAt(position)).equals("+") && operators.contains(""+function.charAt(position))) ||
(position == function.length()-1 && operators.contains(""+function.charAt(position)))){
throw new IllegalArgumentException("Operators are misused in the function");
}
if (currentOperator.contains(function.substring(position, position+1)) & position != 0) {
int firstTermBeginIndex = position;
while (firstTermBeginIndex > 0) {
if ((alphanumeric.contains(""+function.charAt(firstTermBeginIndex))) & (operators.contains(""+function.charAt(firstTermBeginIndex-1)))){
break;
}
firstTermBeginIndex--;
}
if (firstTermBeginIndex != 0 && (function.charAt(firstTermBeginIndex-1) == '-' | function.charAt(firstTermBeginIndex-1) == '+')) {
if (firstTermBeginIndex == 1) {
firstTermBeginIndex--;
}else if (operators.contains(""+(function.charAt(firstTermBeginIndex-2)))){
firstTermBeginIndex--;
}
}
String firstTerm = function.substring(firstTermBeginIndex,position);
int secondTermLastIndex = position;
while (secondTermLastIndex < function.length()-1) {
if ((alphanumeric.contains(""+function.charAt(secondTermLastIndex))) & (operators.contains(""+function.charAt(secondTermLastIndex+1)))) {
break;
}
secondTermLastIndex++;
}
String secondTerm = function.substring(position+1,secondTermLastIndex+1);
double result;
switch (function.substring(position,position+1)) {
case "*": result = solveSingleValue(firstTerm,values)*solveSingleValue(secondTerm,values); break;
case "/": result = solveSingleValue(firstTerm,values)/solveSingleValue(secondTerm,values); break;
case "+": result = solveSingleValue(firstTerm,values)+solveSingleValue(secondTerm,values); break;
case "-": result = solveSingleValue(firstTerm,values)-solveSingleValue(secondTerm,values); break;
case "^": result = Math.pow(solveSingleValue(firstTerm,values),solveSingleValue(secondTerm,values)); break;
default: throw new IllegalArgumentException("Unknown operator: "+currentOperator);
}
String newAttribute = getNewKey(values);
values.put(newAttribute, result);
function = function.substring(0,firstTermBeginIndex)+newAttribute+function.substring(secondTermLastIndex+1,function.length());
deleteValueIfPossible(firstTerm, values, motherFunction);
deleteValueIfPossible(secondTerm, values, motherFunction);
position = -1;
}
position++;
}
}while (operatorIndex < operators.length());
return solveSingleValue(function, values);
}
private static double solveSingleValue (String singleValue, HashMap<String, Double> values) throws IllegalArgumentException{
if (isDouble(singleValue)) {
return Double.parseDouble(singleValue);
}else if (firstContainsOnlySecond(singleValue, alphabetic)){
return getValueFromVariable(singleValue, values);
}else if (firstContainsOnlySecond(singleValue, alphanumeric+"-+")) {
String[] composition = splitByLettersAndNumbers(singleValue);
if (composition.length != 2) {
throw new IllegalArgumentException("Wrong expression: "+singleValue);
}else {
if (composition[0].equals("-")) {
composition[0] = "-1";
}else if (composition[1].equals("-")) {
composition[1] = "-1";
}else if (composition[0].equals("+")) {
composition[0] = "+1";
}else if (composition[1].equals("+")) {
composition[1] = "+1";
}
if (isDouble(composition[0])) {
return Double.parseDouble(composition[0])*getValueFromVariable(composition[1], values);
}else if (isDouble(composition[1])){
return Double.parseDouble(composition[1])*getValueFromVariable(composition[0], values);
}else {
throw new IllegalArgumentException("Wrong expression: "+singleValue);
}
}
}else {
throw new IllegalArgumentException("Wrong expression: "+singleValue);
}
}
private static double getValueFromVariable (String variable, HashMap<String, Double> values) throws IllegalArgumentException{
Double val = values.get(variable);
if (val == null) {
throw new IllegalArgumentException("Unknown variable: "+variable);
}else {
return val;
}
}
/*
* FunctionSolver help tools:
*
*/
private static final String alphabetic = "abcdefghilmnopqrstuvzwykxy";
private static final String numeric = ".";
private static final String alphanumeric = alphabetic+numeric;
private static final String operators = "^*/+-"; //--> Operators order in important!
private static boolean firstContainsOnlySecond(String firstString, String secondString) {
for (int j = 0 ; j < firstString.length() ; j++) {
if (!secondString.contains(firstString.substring(j, j+1))) {
return false;
}
}
return true;
}
private static String getNewKey (HashMap<String, Double> hashMap) {
String alpha = "abcdefghilmnopqrstuvzyjkx";
for (int j = 0 ; j < alpha.length() ; j++) {
String k = alpha.substring(j,j+1);
if (!hashMap.containsKey(k) & !Arrays.asList(usableMathMethods()).contains(k)) {
return k;
}
}
for (int j = 0 ; j < alpha.length() ; j++) {
for (int i = 0 ; i < alpha.length() ; i++) {
String k = alpha.substring(j,j+1)+alpha.substring(i,i+1);
if (!hashMap.containsKey(k) & !Arrays.asList(usableMathMethods()).contains(k)) {
return k;
}
}
}
throw new NullPointerException();
}
public static String[] usableMathMethods() {
/*
* Only methods that:
* return a double type
* present one or two parameters (which are double type)
*/
Method[] mathMethods = Math.class.getDeclaredMethods();
ArrayList<String> usableMethodsNames = new ArrayList<>();
for (Method method : mathMethods) {
boolean usable = true;
int argumentsCounter = 0;
Class<?>[] methodParametersTypes = method.getParameterTypes();
for (Class<?> parameter : methodParametersTypes) {
if (!parameter.getSimpleName().equalsIgnoreCase("double")) {
usable = false;
break;
}else {
argumentsCounter++;
}
}
if (!method.getReturnType().getSimpleName().toLowerCase().equals("double")) {
usable = false;
}
if (usable & argumentsCounter<=2) {
usableMethodsNames.add(method.getName());
}
}
return usableMethodsNames.toArray(new String[usableMethodsNames.size()]);
}
private static boolean isDouble (String number) {
try {
Double.parseDouble(number);
return true;
}catch (Exception ex) {
return false;
}
}
private static String[] splitByLettersAndNumbers (String val) {
if (!firstContainsOnlySecond(val, alphanumeric+"+-")) {
throw new IllegalArgumentException("Wrong passed value: <<"+val+">>");
}
ArrayList<String> response = new ArrayList<>();
String searchingFor;
int lastIndex = 0;
if (firstContainsOnlySecond(""+val.charAt(0), numeric+"+-")) {
searchingFor = alphabetic;
}else {
searchingFor = numeric+"+-";
}
for (int j = 0 ; j < val.length() ; j++) {
if (searchingFor.contains(val.charAt(j)+"")) {
response.add(val.substring(lastIndex, j));
lastIndex = j;
if (searchingFor.equals(numeric+"+-")) {
searchingFor = alphabetic;
}else {
searchingFor = numeric+"+-";
}
}
}
response.add(val.substring(lastIndex,val.length()));
return response.toArray(new String[response.size()]);
}
private static void deleteValueIfPossible (String val, HashMap<String, Double> values, String function) {
if (values.get(val) != null & function != null) {
if (!function.contains(val)) {
values.remove(val);
}
}
}
private static int contOccouranceIn (String howManyOfThatString, String inThatString) {
return inThatString.length() - inThatString.replace(howManyOfThatString, "").length();
}
}
Sugeruję zapominając o Java do tego zadania i korzystać z Clojure. Clojure pozwala analizować i/lub kompilować kod w czasie wykonywania (i podczas kompilacji) i uruchamiać go, a także generować kod w czasie kompilacji, a także wiele innych rzeczy, które, nawiasem mówiąc, są całkiem zwyczajne w świecie LISP-a. Java jest zbyt nudna. A Clojure może zrobić wszystko, co potrafi Java, ponieważ działa na JVM (choć istnieją również inne implementacje). I może być mieszany z Javą bezproblemowo, nawet w jednym wspólnym projekcie. –
To jest * nie * duplikat pytania z pytaniem [jak ocenić wyrażenie arytmetyczne] (http://stackoverflow.com/questions/3422673/evaluating-a-math-expression-given-in-string-form), ponieważ konkretnie pyta o eval(). – Raedwald