评估以stringforms给出的mathexpression式

我正在尝试编写一个Java例程来评估来自String值的简单mathexpression式,例如:

  1. "5+3"
  2. "10-40"
  3. "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)); } } 

我已经写了这个算术expression式的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 (这是正确的)


parsing器是一个recursion下降parsing器 ,因此内部为其语法中每个级别的运算符优先级使用单独的parsing方法。 我保持简短,所以很容易修改,但这里有一些想法可能需要扩展它:

  • variables:

    通过在传递给eval方法的variables表(例如Map<String,Double> variables查找名称,可以轻松地将读取函数名称的parsing器位更改为处理自定义Map<String,Double> variables

  • 单独编译和评估:

    如果增加了对variables的支持,如果你想用改变的variables对同样的expression式进行数百万次的评估,而不是每次都parsing它呢? 这是可能的。 首先定义一个用来评估预编译expression式的接口:

     @FunctionalInterface interface Expression { double eval(); } 

    现在改变所有返回double的方法,而是返回一个该接口的实例。 Java 8的lambda语法对此非常有用。 其中一个更改方法的示例:

     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; } } } 

    构build一个表示编译expression式( 抽象语法树 )的Expression对象的recursion树。 然后你可以编译一次,并用不同的值反复评估它:

     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()); } } 
  • 不同的数据types:

    您可以将评估程序更改为使用更强大的function,如BigDecimal ,或实现复数或有理数(分数)的类,而不是double 。 你甚至可以使用Object ,在expression式中允许一些数据types的混合,就像一个真正的编程语言。 🙂


此答案中的所有代码都已发布到公共领域 。 玩的开心!

解决这个问题的正确方法是使用词法分析器和parsing器 。 您可以自己编写简单的版本,或者这些页面也可以链接到Java词法分析器和分析器。

创build一个recursion下降parsing器是一个非常好的学习练习。

HERE是GitHub上名为EvalEx的另一个开源库。

与JavaScript引擎不同,这个库只专注于评估mathexpression式。 此外,该库是可扩展的,支持使用布尔运算符以及括号。

您也可以尝试BeanShell解释器:

 Interpreter interpreter = new Interpreter(); interpreter.eval("result = (7+21*6)/(32-27)"); System.out.println(interpreter.get("result")); 

如果Java应用程序已经访问数据库,而不使用任何其他JAR,则可以轻松地评估expression式。

有些数据库要求你使用一个虚拟表(例如Oracle的“双”表),而其他数据库则允许你在不从任何表中“select”的情况下评估expression式。

例如,在Sql Server或Sqlite中

 select (((12.10 +12.0))/ 233.0) amount 

和在Oracle中

 select (((12.10 +12.0))/ 233.0) amount from dual; 

使用数据库的优点是可以同时评估多个expression式。 此外,大多数数据库将允许您使用高度复杂的expression式,并且还会有一些额外的function,可以根据需要调用。

但是,如果需要单独评估许多单一expression式,尤其是在数据库位于networking服务器上时,性能可能会受到影响。

下面通过使用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"); 

本文指向3种不同的方法,一种是来自Apache的JEXL ,另一种则允许包含对Java对象引用的脚本。

对于我的大学项目,我正在寻找一个支持基本公式和更复杂的方程(特别是迭代运算符)的parsing器/评估器。 我发现了非常好的JAVA和.NET开源库mXparser。 我会举几个例子来说一些关于语法的东西,进一步的说明请访问项目网站(特别是教程部分)。

http://mathparser.org/

http://mathparser.org/mxparser-tutorial/

http://mathparser.org/api/

还有几个例子

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

 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() 

最好的祝福

JEP似乎应该做这个工作

另一种方法是使用Springexpression式语言或SpEL,它可以在评估mathexpression式的时候做更多的事情,因此可能会略微矫枉过正。 因为它是独立的,你不必使用Spring框架来使用这个expression式库。 从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例子和完整的文档

这是另一个有趣的selecthttps://github.com/Shy-Ta/expression-evaluator-demo

用法非常简单,可以完成工作,例如:

  ExpressionsEvaluator evalExpr = ExpressionsFactory.create("2+3*4-6/2"); assertEquals(BigDecimal.valueOf(11), evalExpr.eval()); 

我认为你做这个事情的方式将涉及到很多条件陈述。 但是对于你的例子中的单个操作,如果有类似的语句,你可以将它限制为4

 String math = "1+4"; if (math.split("+").length == 2) { //do calculation } else if (math.split("-").length == 2) { //do calculation } ... 

当你想处理像“4 + 5 * 6”这样的多个操作时,它会变得更加复杂。

如果你正在试图build立一个计算器,那么我会滔滔不绝地把计算的每个部分(每个数字或运算符)而不是一个string。

如果我们要实现它,那么我们可以使用下面的algorithm:

  1. 虽然还有令牌可以读入,

    1.1获取下一个标记。 1.2如果令牌是:

    1.2.1一个数字:把它推到数值堆栈上。

    1.2.2一个variables:获取它的值,并推入到值栈中。

    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运营商(称之为OO):

      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. 
  2. 操作员堆栈不空时,1从操作员堆栈中popup操作员。 2两次popup数值栈,得到两个操作数。 3按照正确的顺序将操作符应用于操作数。 4将结果推送到值栈中。

  3. 此时操作符栈应该是空的,值栈应该只有一个值,这是最终的结果。

你可能会看看Symja框架 :

 ExprEvaluator util = new ExprEvaluator(); IExpr result = util.evaluate("10-40"); System.out.println(result.toString()); // -> "-30" 

请注意,可以评估明确更复杂的expression式:

 // 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 

这实际上是对@Boann给出的答案的补充。 它有一个轻微的错误,导致“-2 ^ 2”给-4.0的错误结果。 这个问题就是在他的评估中求幂的地步。 只要将幂指数移到parseTerm()的块中,就可以了。 看看下面,这是@Banan的答案稍作修改。 修改是在评论中。

 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(); } 

怎么样这样的事情:

 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); } } 

并相应地为其他math运算符做类似的事情。

使用Djikstra的分stream码algorithm,可以将中缀表示法中的任何expression式string转换为后缀表示法。 algorithm的结果可以作为后缀algorithm的input,返回expression式的结果。

我在这里写了一篇文章,用java实现

现在回答已经太迟了,但是我遇到了同样的情况来评估java中的expression式,这可能会帮助某人

MVEL对expression式进行运行时评估,我们可以在String编写一个java代码,以便在此处进行评估。

  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); 
 import java.util.*; StringTokenizer st; int ans; public class check { 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; } //......... } 

还有一个select: https : //github.com/stefanhaustein/expressionparser

我已经实现了这个有一个简单但灵活的选项,以允许:

  • 立即处理( Calculator.java , SetDemo.java )
  • 构build并处理一个分析树( TreeBuilder.java )

上面链接的TreeBuilder是CAS演示程序包的一部分 ,可以进行符号派生。 还有一个BASIC解释器的例子,我已经开始使用它来构build一个TypeScript解释器。

使用JDK1.6的JavaScript引擎和代码注入处理,尝试下面的示例代码。

 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; } } } 

包ExpressionCalculator.expressioncalculator;

import java.text.DecimalFormat; import java.util.Scanner;

公共类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, ie 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)); } 

}

一个可以评估mathexpression式的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的外部库可以用来运行JavaScript。 而javascript可以评估简单的公式,而不用限制string。 如果代码编写得好,对性能没有影响。 下面是一个与犀牛的例子 –

 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); } 
 public class StringCalculator { public static void main(String[] args) { String eval = "2+3*2/2+2*5/5*5"; System.out.println(calculator(eval)); } public static int calcMulAndDiv(String val){ String nos[] = val.split("\\D+"); String opr[] = val.split("\\d+"); int res = Integer.parseInt(nos[0]); for(int i = 1; i< opr.length ;i++){ if(opr[i].equals("*")){ res = res * Integer.parseInt(nos[i]); } else if(opr[i].equals("/")){ res = res / Integer.parseInt(nos[i]); } } return res; } public static int calculator(String val){ String nos[] = val.split("[+-]"); String operators = val.replaceAll("[^+-]",""); char opr[] = operators.toCharArray(); int result = 0; if(nos[0].contains("*") || nos[0].contains("*")){ result = calcMulAndDiv(nos[0]); }else{ result = Integer.parseInt(nos[0]); } for(int i = 0 ; i < opr.length ; i++){ if(opr[i] == '+'){ if(nos[i+1].contains("*") || nos[i+1].contains("*")){ result = result + calcMulAndDiv(nos[i+1]); }else{ result = result + Integer.parseInt(nos[i+1]); } } else if(opr[i] == '-'){ if(nos[i+1].contains("*") || nos[i+1].contains("*")){ result = result + calcMulAndDiv(nos[i+1]); }else{ result = result - Integer.parseInt(nos[i+1]); } } } return result; } } 
 public static void main(String[] args){ System.out.println("="+evaluate(args[2])); } public static String[] symbols = new String[]{"\\-","\\+","\\/","\\*"}; public static Integer evaluate(String exp){ System.out.print(exp); exp = exp.trim(); boolean isint = true; for(int i = 0; i<exp.length() && isint;i++){ if(exp.charAt(i)>'9' || exp.charAt(i) <'0'){ isint = false; } } if(isint) return Integer.parseInt(exp); for(String symbol:symbols){ String[] split = exp.split(symbol); if(split.length>1){ int ev = evaluate(split[0]); for(int i = 1;i<split.length;i++){ System.out.print(symbol); int val = evaluate(split[i]); if("\\*".equals(symbol)) ev*=val; if("\\/".equals(symbol)) ev/=val; if("\\+".equals(symbol)) ev+=val; if("\\-".equals(symbol)) ev-=val; } return ev; } } return null; }