expr4j学习

一、介绍

exp4j 能够评估实域中的表达式和函数。这是一个小型 (40KB) 库,没有任何外部依赖项,实现了Dijkstra 的 Shutting Yard Algorithm。exp4j 带有一组标准的内置函数和运算符。此外,用户可以创建自定义运算符和函数。

exp4j 能够支持excel中的十几种数据函数和最基础的运算符号,并且用户可以自定义函数和运算符,扩展也比较方便容易,只不过都是使用的double最为的基础数据类型,如果精度特别高的,可以自己可以把源码下载下来,然后对项目进行修改。

官网地址:https://www.objecthunter.net/exp4j/

github: https://github.com/fasseg/exp4j

二、坐标依赖

1、依赖

<dependency> 
    <groupId>net.objecthunter</groupId> 
    <artifactId>exp4j</artifactId> 
    <version>0.4.8</version> 
</dependency>

2、最全阿里镜像

大家在依赖坐标的时候可能会下载不下来,推荐配置上阿里的所有镜像仓库,可以再阿里仓库官网查看

https://developer.aliyun.com/mvn/guide

<?xml version="1.0" encoding="UTF-8"?>


<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">

  <localRepository>D:/Maven/repository</localRepository>
  

  <mirrors>
   	<mirror>
		<id>aliyun-central</id>
		<mirrorOf>central</mirrorOf>
		<name>aliyun central</name>
		<url>https://maven.aliyun.com/repository/central</url>
	</mirror>
    <mirror>
		<id>aliyun-public</id>
		<mirrorOf>public</mirrorOf>
		<name>aliyun public</name>
		<url>https://maven.aliyun.com/repository/public</url>
	</mirror>
	 
 <mirror>
		<id>aliyun-public</id>
		<mirrorOf>jcenter</mirrorOf>
		<name>aliyun jcenter</name>
		<url>https://maven.aliyun.com/repository/public</url>
	</mirror>


	<mirror>
		<id>aliyun-spring</id>
		<mirrorOf>aliyun-spring</mirrorOf>
		<name>aliyun spring</name>
		<url>https://maven.aliyun.com/repository/spring</url>
	</mirror>

	<mirror>
		<id>aliyun-spring-plugin</id>
		<mirrorOf>aliyun-spring-plugin</mirrorOf>
		<name>aliyun spring-plugin</name>
		<url>https://maven.aliyun.com/repository/spring-plugin</url>
	</mirror>

	<mirror>
		<id>aliyun-apache-snapshots</id>
		<mirrorOf>aliyun-apache-snapshots</mirrorOf>
		<name>aliyun apache-snapshots</name>
		<url>https://maven.aliyun.com/repository/apache-snapshots</url>
	</mirror>

	<mirror>
		<id>aliyun-google</id>
		<mirrorOf>aliyun-google</mirrorOf>
		<name>aliyun google</name>
		<url>https://maven.aliyun.com/repository/google</url>
	</mirror>

	<mirror>
		<id>aliyun-gradle-plugin</id>
		<mirrorOf>aliyun-gradle-plugi</mirrorOf>
		<name>aliyun gradle-plugin</name>
		<url>https://maven.aliyun.com/repository/gradle-plugin</url>
	</mirror>

	<mirror>
		<id>aliyun-jcenter</id>
		<mirrorOf>aliyun-jcenter</mirrorOf>
		<name>aliyun jcenter</name>
		<url>https://maven.aliyun.com/repository/jcenter</url>
	</mirror>

	<mirror>
		<id>aliyun-releases</id>
		<mirrorOf>aliyun-releases</mirrorOf>
		<name>aliyun releases</name>
		<url>https://maven.aliyun.com/repository/releases</url>
	</mirror>

	<mirror>
		<id>aliyun-snapshots</id>
		<mirrorOf>aliyun-snapshots</mirrorOf>
		<name>aliyun snapshots</name>
		<url>https://maven.aliyun.com/repository/snapshots</url>
	</mirror>

	<mirror>
		<id>aliyun-grails-core</id>
		<mirrorOf>aliyun-grails-core</mirrorOf>
		<name>aliyun grails-core</name>
		<url>https://maven.aliyun.com/repository/grails-core</url>
	</mirror>

	<mirror>
		<id>aliyun-mapr-public</id>
		<mirrorOf>aliyun-mapr-public</mirrorOf>
		<name>aliyun mapr-public</name>
		<url>https://maven.aliyun.com/repository/mapr-public</url>
	</mirror>
  </mirrors>
  <profiles>
  </profiles>
</settings>

三、exp4j 支持丰富的表达式

## 1、评估表达式

为了评估一个表达式。ExpressionBuilder类可用于创建能够评估的Expression对象。可以通过调用ExpressionBuilder.function()ExpressionBuilder.operator()来设置自定义函数和自定义运算符。任何给定的变量都可以通过调用Expression.variable()在 ExpressionBuilder.build( ``)返回的``Expression对象上设置

/**
    * SIN函数 一秒计算正弦值
    */
   @Test
   public void test3() {
      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();
      System.out.println(result);//-6.66188870791721
   }

2、异步计算表达式

   /**
    * 异步计算表达式,巧用LOG函数 按指定底数返回对数
    LOG(number,base) 
Number 为用于计算对数的正实数。 
base 为对数的底数。如果省略底数,则假定其值为10。
    */
   @Test
   public void test4() throws ExecutionException, InterruptedException {
      ExecutorService exec = Executors.newFixedThreadPool(1);
      Expression e = new ExpressionBuilder("3log(y)/(x+1)")
         .variables("x", "y")
         .build().setVariable("x", 2.3).setVariable("y", 3.14);
      Future<Double> future = e.evaluateAsync(exec);
      double result = future.get();
      System.out.println(result);//1.0402025453819654
   }

3、变量声明

变量名必须以字母或下划线_开头,并且只能包含字母、数字或下划线。

以下是有效的变量名:

  • varX

  • _x1

  • _var_X_1

    1_var_x不是因为它不以字母或下划线开头。

4、隐式乘法

因此,像2cos(yx)这样的表达式将被解释为2max(yx)

  /**
    *,像2cos(yx)这样的表达式将被解释为2*cos(y*x)
    */
   @Test
   public void test5() throws ExecutionException, InterruptedException {
      double result = new ExpressionBuilder("2cos(xy)")
         .variables("x","y") .build
            ()
         .setVariable("x", 0.5d)
         .setVariable("y", 0.25d)
         .evaluate ();
      System.out.println(result);//1.984395334458658
   }

5、数值常数

自 0.4.6 版起,以下常用常数已添加到 exp4j 并自动绑定:pi, π Math.PI 中定义的 π 值,e欧拉数 e 的值,φ黄金比例 (1.61803398874) 的值)

String expr = "pi+π+e+φ";
double expected = 2*Math.PI + Math.E + 1.61803398874d;
Expression e = new ExpressionBuilder(expr).build();
assertEquals(expected, e.evaluate(),0d);

6、科学计数法

从 0.3.5 版开始,可以使用科学记数法来表示数字,请参阅 wikipedia。该数字分为有效数/尾数y和``yEx形式的指数x ,计算为y * 10^x。请注意,‘e/E’ 不是运算符,而是数字的一部分,因此无法评估像1.1e-(x*2)这样的表达式。使用精细结构常数α=7.2973525698 * 10^−3的示例:

  //科学计数法
   @Test
   public void test7() {
      String expr = "7.2973525698e-3";
      double expected = Double.parseDouble(expr);
      Expression e = new ExpressionBuilder(expr).build();
      assertEquals(expected, e.evaluate(),0d);
      System.out.println(expected);//0.0072973525698
   }

7、自定义函数

您可以扩展抽象类 Function 以便在表达式中使用自定义函数。你只需要实现apply(double... args)方法。在以下示例中,创建了一个以 2 为底的对数函数,并在以下表达式中使用。

使用恒等式计算任意底的对数的自定义函数log(value, base) = ln(value)/ln(base)

   //自定义函数 logb函数 按指定底数返回对数
   @Test
   public void test8() {
      Function logb = new Function("logb", 2) {
         @Override
         public double apply(double... args) {
            return Math.log(args[0]) / Math.log(args[1]);
         }
      };
      double result = new ExpressionBuilder("logb(8, 2)")
         .function(logb)
         .build()
         .evaluate();
      double expected = 3;
      assertEquals(expected, result, 0d);
      System.out.println(expected);//3.0
   }

8、自定义平均函数:

//自定义4个平均函数 avg函数
   @Test
   public void test9() {
      Function avg = new Function("avg", 4) {

         @Override
         public double apply(double... args) {
            double sum = 0;
            for(double arg : args) {
               sum += arg;
            }
            return sum / args.length;
         }
      };
      double result = new ExpressionBuilder("avg(1,2,3,4)").function(avg).build().evaluate();
      System.out.println(result);//2.5
      double expected = 2.5d;
      assertEquals(expected, result, 0d);
   }

9、自定义运算符

您可以扩展抽象类Operator以声明用于表达式的自定义运算符,符号是由!,#,§,$,&,;,:,~,<,>,|,= 组成的字符串。. 请注意,添加带有已使用符号的运算符会覆盖任何现有运算符,包括内置运算符。因此可以覆盖例如+运算符。Operator 的构造函数最多接受 4 个参数:

  • 用于此操作的符号(由!,#,§,$,&,;,:,~,<,>,|,=组成的字符串)

  • 如果操作是左关联的

  • 操作的优先级

  • 运算符的操作数(1 或 2

    //自定义运算符,创建用于计算阶乘的自定义运算符
    @Test
    public void test10() {
       Operator factorial = new Operator("!", 1, true, Operator.PRECEDENCE_POWER + 1) {
    
          @Override
          public double apply(double... args) {
             final int arg = (int) args[0];
             if ((double) arg != args[0]) {
                throw new IllegalArgumentException("Operand for factorial has to be an integer");
             }
             if (arg < 0) {
                throw new IllegalArgumentException("The operand of the factorial can not be less than zero");
             }
             double result = 1;
             for (int i = 1; i <= arg; i++) {
                result *= i;
             }
             return result;
          }
       };
    
       double result = new ExpressionBuilder("3!")
          .operator(factorial)
          .build()
          .evaluate();
    
       double expected = 6d;
       assertEquals(expected, result, 0d);
    }
    

创建一个自定义运算符逻辑运算符,如果第一个参数大于或等于第二个参数,则返回 1,否则返回 0。

   /**
    * 创建一个自定义运算符逻辑运算符,如果第一个参数大于或等于第二个参数,则返回 1,否则返回 0。
    * @throws Exception
    */
   @Test
   public void testOperators3() throws Exception {
      Operator gteq = new Operator(">=", 2, true, Operator.PRECEDENCE_ADDITION - 1) {

         @Override
         public double apply(double[] values) {
            if (values[0] >= values[1]) {
               return 1d;
            } else {
               return 0d;
            }
         }
      };

      Expression e = new ExpressionBuilder("1>=2").operator(gteq)
                                                  .build();
      double evaluate = e.evaluate();
      System.out.println(evaluate);
      assertTrue(0d == e.evaluate());
      e = new ExpressionBuilder("2>=1").operator(gteq)
                                       .build();
      assertTrue(1d == e.evaluate());
   }

10、内置运算符

  • 加法:2 + 2
  • 减法:2 - 2
  • 乘法:2 * 2
  • 除法:2 / 2
  • 指数:2 ^ 2
  • 一元减号,加号(符号运算符):+2 - (-2)
  • 模量:2% 2

一元减号和幂运算符的优先级

一元减号运算符的优先级低于幂运算符的优先级。这意味着像-1^2这样的表达式被评估为-(1^2)而不是(-1)^2

运算和功能除以零

exp4j 在尝试除以零时抛出 ArithmeticException。在实现涉及除法的 Operator 或 Function 时,实现者必须确保抛出相应的 RuntimeException。

   //Division by zero 除零的异常
   @Test
   public void test11() {

      Operator reciprocal = new Operator("$", 1, true, Operator.PRECEDENCE_DIVISION) {
         @Override
         public double apply(final double... args) {
            if (args[0] == 0d) {
               throw new ArithmeticException("Division by zero!");
            }
            return 1d / args[0];
         }
      };
      Expression e = new ExpressionBuilder("0$").operator(reciprocal).build();
      double evaluate = e.evaluate();// <- this call will throw an ArithmeticException
      System.out.println(evaluate);//2.5
   }

11、内置函数

好多函数比较陌生,想了解的自己百度一下吧

  • abs: absolute value 绝对值
  • acos: arc cosine 反余弦
  • asin: arc sine 反正弦
  • atan: arc tangent 反正切
  • cbrt: cubic root 立方根
  • ceil: nearest upper integer 最接近的上整数
  • cos: cosine 余弦
  • cosh: hyperbolic cosine 双曲余弦
  • exp: euler’s number raised to the power (e^x) 欧拉数的幂 (e^x)
  • floor: nearest lower integer 最接近的下整数
  • log: logarithmus naturalis (base e) 自然对数(以 e 为底)
  • log10: logarithm (base 10) 对数(以 10 为底)
  • log2: logarithm (base 2) 对数(以 2 为底)
  • sin: sine
  • sinh: hyperbolic sine
  • sqrt: square root 双曲正弦
  • tan: tangent 切线
  • tanh: hyperbolic tangent 双曲正切
  • signum: signum function 符号函数

12 、表达式验证

从 0.4.0 版本开始,exp4j 增加了一个验证表达式的功能。用户可以调用Expression.validate()来执行相对快速的验证。validate 方法还接受一个布尔参数,指示是否应该检查空变量。

   @Test
   public void test12() {
      Expression e = new ExpressionBuilder("x")
         .variable("x")
         .build();
      ValidationResult res = e.validate();
      assertFalse(res.isValid());
      assertEquals(1, res.getErrors().size());
      e.setVariable("x",1d);
      res = e.validate();
      assertTrue(res.isValid());
   }
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐