Fel实现自定义计算引擎,平均计算速度每秒10w

Fel的介绍和简单使用以及0.9版本的包在之前的博客写过了,有需要的朋友移步:fel的简单使用及介绍

项目地址:https://github.com/an-yusheng/CSDN-FelDemo

 

1. 基础的应用

下面的代码是引用原生类进行执行的,支持通用数学函数即加减乘除

package com.example.demo.util;

import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.FelEngineImpl;

/**
 * @author Anzepeng
 * @title: FelDemo
 * @projectName demo
 * @description: TODO
 * @date 2020/6/3 0003下午 13:42
 */
public class FelDemo {

    public static void main(String[] args){
        // 计算核心类
        FelEngine felEngine = new FelEngineImpl();
        // 执行计算的方法eval(公式)
        Object result = felEngine.eval("1+1");
        // 结果
        System.out.println("计算结果为:"+result);
    }
}

执行结果

如果仅仅是普通运算那并不能满足我们的需求,尝试一下聚合函数

错误信息找不到函数,原生包并没有支持这些函数,所以我们要重写它的实现代码

2. 查看源代码

FelEngineImpl的源代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.greenpineyu.fel;

import com.greenpineyu.fel.common.FelBuilder;
import com.greenpineyu.fel.compile.CompileService;
import com.greenpineyu.fel.context.ArrayCtxImpl;
import com.greenpineyu.fel.context.FelContext;
import com.greenpineyu.fel.context.Var;
import com.greenpineyu.fel.function.FunMgr;
import com.greenpineyu.fel.function.Function;
import com.greenpineyu.fel.optimizer.Optimizer;
import com.greenpineyu.fel.optimizer.VarVisitOpti;
import com.greenpineyu.fel.parser.AntlrParser;
import com.greenpineyu.fel.parser.FelNode;
import com.greenpineyu.fel.parser.Parser;
import com.greenpineyu.fel.security.SecurityMgr;

public class FelEngineImpl implements FelEngine {
    private FelContext context;
    private CompileService compiler;
    private Parser parser;
    private FunMgr funMgr;
    private SecurityMgr securityMgr;

    public SecurityMgr getSecurityMgr() {
        return this.securityMgr;
    }

    public void setSecurityMgr(SecurityMgr var1) {
        this.securityMgr = var1;
    }

    public FelEngineImpl(FelContext var1) {
        this.securityMgr = FelBuilder.newSecurityMgr();
        this.context = var1;
        this.compiler = new CompileService();
        this.parser = new AntlrParser(this);
        this.funMgr = new FunMgr();
    }

    public FelEngineImpl() {
        this(new ArrayCtxImpl());
    }

    public FelNode parse(String var1) {
        return this.parser.parse(var1);
    }

    public Object eval(String var1) {
        return this.eval(var1, this.context);
    }

    public Object eval(String var1, Var... var2) {
        FelNode var3 = this.parse(var1);
        VarVisitOpti var4 = new VarVisitOpti(var2);
        var3 = var4.call(this.context, var3);
        return var3.eval(this.context);
    }

    public Object eval(String var1, FelContext var2) {
        return this.parse(var1).eval(var2);
    }

    public Expression compile(String var1, Var... var2) {
        return this.compile(var1, (FelContext)null, new VarVisitOpti(var2));
    }

    public Expression compile(String var1, FelContext var2, Optimizer... var3) {
        if (var2 == null) {
            var2 = this.context;
        }

        FelNode var4 = this.parse(var1);
        if (var3 != null) {
            Optimizer[] var5 = var3;
            int var6 = var3.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Optimizer var8 = var5[var7];
                if (var8 != null) {
                    var4 = var8.call(var2, var4);
                }
            }
        }

        return this.compiler.compile(var2, var4, var1);
    }

    public String toString() {
        return "FelEngine";
    }

    public void addFun(Function var1) {
        this.funMgr.add(var1);
    }

    public FelContext getContext() {
        return this.context;
    }

    public CompileService getCompiler() {
        return this.compiler;
    }

    public void setCompiler(CompileService var1) {
        this.compiler = var1;
    }

    public Parser getParser() {
        return this.parser;
    }

    public void setParser(Parser var1) {
        this.parser = var1;
    }

    public FunMgr getFunMgr() {
        return this.funMgr;
    }

    public void setFunMgr(FunMgr var1) {
        this.funMgr = var1;
    }

    public void setContext(FelContext var1) {
        this.context = var1;
    }
}

3. 重写FelEngineImpl类并实现FelEngine

下面是我们创建可修改的实现类,跟源代码不一致的地方是我将 this(new ArrayCtxImpl()); 注释了

package com.example.demo.util;


import com.greenpineyu.fel.Expression;
import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.common.FelBuilder;
import com.greenpineyu.fel.compile.CompileService;
import com.greenpineyu.fel.context.ArrayCtxImpl;
import com.greenpineyu.fel.context.FelContext;
import com.greenpineyu.fel.context.Var;
import com.greenpineyu.fel.function.FunMgr;
import com.greenpineyu.fel.function.Function;
import com.greenpineyu.fel.optimizer.Optimizer;
import com.greenpineyu.fel.optimizer.VarVisitOpti;
import com.greenpineyu.fel.parser.AntlrParser;
import com.greenpineyu.fel.parser.FelNode;
import com.greenpineyu.fel.parser.Parser;
import com.greenpineyu.fel.security.SecurityMgr;

/**
 * @author Anzepeng
 * @title: FelEngineImpl
 * @projectName calculate
 * @description: TODO
 * @date 2020/5/20 0020下午 17:44
 */
public class FelEngineImpl implements FelEngine {
    private FelContext context;
    private CompileService compiler;
    private Parser parser;
    private FunMgr funMgr;
    private SecurityMgr securityMgr;

    public SecurityMgr getSecurityMgr() {
        return this.securityMgr;
    }

    public void setSecurityMgr(SecurityMgr var1) {
        this.securityMgr = var1;
    }

    public FelEngineImpl(FelContext var1) {
        this.securityMgr = FelBuilder.newSecurityMgr();
        this.context = var1;
        this.compiler = new CompileService();
        this.parser = new AntlrParser(this);
        this.funMgr = new FunMgr();
    }

    public FelEngineImpl() {
        //this(new ArrayCtxImpl());
    }

    public FelNode parse(String var1) {
        return this.parser.parse(var1);
    }

    public Object eval(String var1) {
        return this.eval(var1, this.context);
    }

    public Object eval(String var1, Var... var2) {
        FelNode var3 = this.parse(var1);
        VarVisitOpti var4 = new VarVisitOpti(var2);
        var3 = var4.call(this.context, var3);
        return var3.eval(this.context);
    }

    public Object eval(String var1, FelContext var2) {
        return this.parse(var1).eval(var2);
    }

    public Expression compile(String var1, Var... var2) {
        return this.compile(var1, (FelContext)null, new VarVisitOpti(var2));
    }

    public Expression compile(String var1, FelContext var2, Optimizer... var3) {
        if (var2 == null) {
            var2 = this.context;
        }

        FelNode var4 = this.parse(var1);
        if (var3 != null) {
            Optimizer[] var5 = var3;
            int var6 = var3.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Optimizer var8 = var5[var7];
                if (var8 != null) {
                    var4 = var8.call(var2, var4);
                }
            }
        }

        return this.compiler.compile(var2, var4, var1);
    }

    public String toString() {
        return "FelEngine";
    }

    public void addFun(Function var1) {
        this.funMgr.add(var1);
    }

    public FelContext getContext() {
        return this.context;
    }

    public CompileService getCompiler() {
        return this.compiler;
    }

    public void setCompiler(CompileService var1) {
        this.compiler = var1;
    }

    public Parser getParser() {
        return this.parser;
    }

    public void setParser(Parser var1) {
        this.parser = var1;
    }

    public FunMgr getFunMgr() {
        return this.funMgr;
    }

    public void setFunMgr(FunMgr var1) {
        this.funMgr = var1;
    }

    public void setContext(FelContext var1) {
        this.context = var1;
    }
}

在demo类里将引用的FelEngineImpl类改成我们创建的这个然后执行

出现错误java.lang.NullPointerException

解开注释再次执行,恢复正常

这个方法里写的就是我们定义好的运算,函数或方法,Parser解析公式后执行的运算就是匹配这个方法里的内容

4. 编写自定义函数

在上面知道运算方法后我们就要加入自定义函数了,fel的简单使用及介绍  里面也讲过自定义函数了,我们直接拿过来修改一下

我们只取第一部分代码并修改一下,改成sum的函数,并添加函数引用,代码如下

public FelEngineImpl() {
        this(new ArrayCtxImpl());
        // sum函数
        Function sumFun = new CommonFunction() {
            public String getName() {
                return "sum";
            }
            @Override
            public Object call(Object... values) {
                Double sum=0d;
                for(Object o:values){
                    if(o!=null){
                        Double d= NumberUtil.toDouble(o);
                        sum+=d;
                    }
                }
                return sum;
            }
        };
        // 添加sum函数
        this.addFun(sumFun);
    }

修改完成,执行一下sum函数

5. 重写后的FelEngineImpl代码执行示例

支持的运算 、函数、判断

普通运算:加减乘除

聚合函数:MAX、MIN、AVG、SUM、、、等

关系:大于、小于、大于等于、小于等于、等于、不等于等

条件:?:

6. 百万数据速度测试

因为输出所有结果会影响真实速度,我们先不输出测试一下

百万数据计算速度10161ms,平均每秒10w,我们再输出一下公式结果,确定一下计算结果正确性

结果应该没有错误,因为有输出所以多了8s的时间

7. FelEngineImpl代码

package com.example.demo.util;


import com.greenpineyu.fel.Expression;
import com.greenpineyu.fel.FelEngine;
import com.greenpineyu.fel.common.FelBuilder;
import com.greenpineyu.fel.common.NumberUtil;
import com.greenpineyu.fel.common.ObjectUtils;
import com.greenpineyu.fel.compile.CompileService;
import com.greenpineyu.fel.context.ArrayCtxImpl;
import com.greenpineyu.fel.context.FelContext;
import com.greenpineyu.fel.context.Var;
import com.greenpineyu.fel.function.CommonFunction;
import com.greenpineyu.fel.function.FunMgr;
import com.greenpineyu.fel.function.Function;
import com.greenpineyu.fel.optimizer.Optimizer;
import com.greenpineyu.fel.optimizer.VarVisitOpti;
import com.greenpineyu.fel.parser.AntlrParser;
import com.greenpineyu.fel.parser.FelNode;
import com.greenpineyu.fel.parser.Parser;
import com.greenpineyu.fel.security.SecurityMgr;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Random;

/**
 * @author Anzepeng
 * @title: FelEngineImpl
 * @projectName calculate
 * @description: TODO
 * @date 2020/5/20 0020下午 17:44
 */
public class FelEngineImpl implements FelEngine {
    private FelContext context;
    private CompileService compiler;
    private Parser parser;
    private FunMgr funMgr;
    private SecurityMgr securityMgr;

    public SecurityMgr getSecurityMgr() {
        return this.securityMgr;
    }

    public void setSecurityMgr(SecurityMgr var1) {
        this.securityMgr = var1;
    }

    public FelEngineImpl(FelContext var1) {
        this.securityMgr = FelBuilder.newSecurityMgr();
        this.context = var1;
        this.compiler = new CompileService();
        this.parser = new AntlrParser(this);
        this.funMgr = new FunMgr();
    }

    public FelEngineImpl() {
        this(new ArrayCtxImpl());
        Function funmax= new CommonFunction() {
            public String getName() {
                return "MAX";
            }
            @Override
            public Object call(Object... values) {
                Double max=0-Double.MAX_VALUE;
                for(Object o:values){
                    Double d=NumberUtil.toDouble(o);
                    if(d.compareTo(max)>0){
                        max=d;
                    }
                }
                return max;
            }
        };
        this.addFun(funmax);
        Function funmin= new CommonFunction() {
            public String getName() {
                return "MIN";
            }
            @Override
            public Object call(Object... values) {
                Double min=Double.MAX_VALUE;
                for(Object o:values){
                    Double d=NumberUtil.toDouble(o);
                    if(d.compareTo(min)<0){
                        min=d;
                    }
                }
                return min;
            }
        };
        this.addFun(funmin);
        Function funavg= new CommonFunction() {
            public String getName() {
                return "AVG";
            }
            @Override
            public Object call(Object... values) {
                Double sum=0d;
                for(Object o:values){
                    Double d=NumberUtil.toDouble(o);
                    sum+=d;
                }
                return sum/values.length;
            }
        };
        this.addFun(funavg);

        Function avgeli= new CommonFunction() {
            public String getName() {
                return "AVGELI";
            }
            @Override
            public Object call(Object... values) {
                Double sum=0d;
                int count=0;
                for(Object o:values){
                    if(o!=null){
                        Double d=NumberUtil.toDouble(o);
                        sum+=d;
                        if(d!=0.0){
                            count++;
                        }
                    }
                }
                return sum/count;
            }
        };


        this.addFun(avgeli);
        Function funsum= new CommonFunction() {
            public String getName() {
                return "SUM";
            }
            @Override
            public Object call(Object... values) {
                Double sum=0d;
                for(Object o:values){
                    if(o!=null){
                        Double d=NumberUtil.toDouble(o);
                        sum+=d;
                    }
                }
                return sum;
            }
        };
        this.addFun(funsum);
        Function funabs= new CommonFunction() {
            public String getName() {
                return "ABS";
            }
            @Override
            public Object call(Object... values) {
                Double result=NumberUtil.toDouble(values[0]);
                return Math.abs(result);
            }
        };
        this.addFun(funabs);
        Function funbadmin= new CommonFunction() {
            public String getName() {
                return "BADMIN";
            }
            @Override
            public Object call(Object... values) {
                Double min=Double.MAX_VALUE;
                boolean a=true;
                for(Object o:values){
                    Double d=NumberUtil.toDouble(o);
                    Double b=Math.abs(d);
                    if(b.compareTo(min)<0){
                        min=b;
                        if(d>=0){
                            a=true;
                        }else{
                            a=false;
                        }
                    }
                }
                if(a){
                    return min;
                }else{
                    return 0-min;
                }
            }
        };
        this.addFun(funbadmin);

        Function funbadmax= new CommonFunction() {
            public String getName() {
                return "BADMAX";
            }
            @Override
            public Object call(Object... values) {
                Double max=0d;
                boolean a=true;
                for(Object o:values){
                    Double d=NumberUtil.toDouble(o);
                    if(d.equals(NumberUtil.toDouble(-100))){
                        continue;
                    }
                    Double b=Math.abs(d);
                    if(b.compareTo(max)>0){
                        if(d>=0){
                            a=true;
                        }else{
                            a=false;
                        }
                        max=b;
                    }
                }
                if(a){
                    return max;
                }else{
                    return 0-max;
                }
            }
        };
        this.addFun(funbadmax);
        Function funacos= new CommonFunction() {
            public String getName() {
                return "ACOS";
            }
            @Override
            public Object call(Object... values) {
                Double result=NumberUtil.toDouble(values[0]);
                return Math.acos(result);
            }
        };
        this.addFun(funacos);
        Function funasin= new CommonFunction() {
            public String getName() {
                return "ASIN";
            }
            @Override
            public Object call(Object... values) {
                Double result=NumberUtil.toDouble(values[0]);
                return Math.asin(result);
            }
        };
        this.addFun(funasin);
        Function funatan= new CommonFunction() {
            public String getName() {
                return "ATAN";
            }
            @Override
            public Object call(Object... values) {
                Double result=NumberUtil.toDouble(values[0]);
                return Math.atan(result);
            }
        };
        this.addFun(funatan);

        Function funcos= new CommonFunction() {
            public String getName() {
                return "COS";
            }
            @Override
            public Object call(Object... values) {
                Double result=NumberUtil.toDouble(values[0]);
                return Math.cos(result);
            }
        };
        this.addFun(funcos);
        Function funsin= new CommonFunction() {
            public String getName() {
                return "SIN";
            }
            @Override
            public Object call(Object... values) {
                Double result=NumberUtil.toDouble(values[0]);
                return Math.sin(result);
            }
        };
        this.addFun(funsin);
        Function funtan= new CommonFunction() {
            public String getName() {
                return "TAN";
            }
            @Override
            public Object call(Object... values) {
                Double result=NumberUtil.toDouble(values[0]);
                return Math.tan(result);
            }
        };
        this.addFun(funtan);

        Function funceil= new CommonFunction() {
            public String getName() {
                return "CEIL";
            }
            @Override
            public Object call(Object... values) {
                Double result=NumberUtil.toDouble(values[0]);
                return Math.ceil(result);
            }
        };
        this.addFun(funceil);

        Function funround= new CommonFunction() {
            public String getName() {
                return "ROUND";
            }
            @Override
            public Object call(Object... values) {
                Double result=NumberUtil.toDouble(values[0]);
                return Math.round(result*100)*0.01;
            }
        };
        this.addFun(funround);

        Function funfloor= new CommonFunction() {
            public String getName() {
                return "FLOOR";
            }
            @Override
            public Object call(Object... values) {
                Double result=NumberUtil.toDouble(values[0]);
                return Math.floor(result);
            }
        };
        this.addFun(funfloor);

        Function funsqrt= new CommonFunction() {
            public String getName() {
                return "SQRT";
            }
            @Override
            public Object call(Object... values) {
                Double result=NumberUtil.toDouble(values[0]);
                return Math.sqrt(result);
            }
        };
        this.addFun(funsqrt);

        Function funpow= new CommonFunction() {
            public String getName() {
                return "POW";
            }
            @Override
            public Object call(Object... values) {
                Double arg1=NumberUtil.toDouble(values[0]);
                return Math.pow(arg1,2);
            }
        };
        this.addFun(funpow);

        Function funpown= new CommonFunction() {
            public String getName() {
                return "POWN";
            }
            @Override
            public Object call(Object... values) {
                Double arg1=NumberUtil.toDouble(values[0]);
                Double arg2=NumberUtil.toDouble(values[1]);
                return Math.pow(arg1,arg2);
            }
        };
        this.addFun(funpown);

        Function funsqrtn= new CommonFunction() {
            public String getName() {
                return "SQRTN";
            }
            @Override
            public Object call(Object... values) {
                Double arg1=NumberUtil.toDouble(values[0]);
                Double arg2=NumberUtil.toDouble(values[1]);
                return Math.pow(arg1,1/arg2);
            }
        };
        this.addFun(funsqrtn);

        Function equals= new CommonFunction() {
            public String getName() {
                return "EQUALS";
            }
            @Override
            public Object call(Object... values) {
                return values[0].equals(values[1]);
            }
        };
        this.addFun(equals);

        Function isnull= new CommonFunction() {
            public String getName() {
                return "ISNULL";
            }
            @Override
            public Object call(Object... values) {
                if(values==null||values[0]==null||"".equals(values[0].toString().trim())){
                    return true;
                }
                return false;
            }
        };
        this.addFun(isnull);


        Function nulltozero= new CommonFunction() {
            public String getName() {
                return "NULLTOZERO";
            }
            @Override
            public Object call(Object... values) {
                if(values==null||values[0]==null||"".equals(values[0].toString().trim())){
                    return 0;
                }
                return values[0];
            }
        };
        this.addFun(nulltozero);

        Function concat= new CommonFunction() {
            public String getName() {
                return "CONCAT";
            }
            @Override
            public Object call(Object... values) {
                StringBuffer result=new StringBuffer();
                for(Object v:values){
                    if(v!=null)
                        result.append(v);
                }
                return result.toString();
            }
        };
        this.addFun(concat);

        Function date= new CommonFunction() {
            public String getName() {
                return "DATE";
            }
            @Override
            public Object call(Object... values) {
                SimpleDateFormat format=null;
                if(values==null){
                    format=new SimpleDateFormat("yyyy-MM-dd");
                } else if(values[0].toString().equals("1")){
                    format=new SimpleDateFormat("yyyy-MM-dd-");
                } else if(values[0].toString().equals("2")){
                    format=new SimpleDateFormat("yyyy-MM");
                } else if(values[0].toString().equals("3")){
                    format=new SimpleDateFormat("yyyy");
                }
                return format.format(new Date(System.currentTimeMillis()));
            }
        };
        this.addFun(date);


        Function random= new CommonFunction() {
            public String getName() {
                return "RANDOM";
            }
            @Override
            public Object call(Object... values) {
                Random random1 = new Random(System.currentTimeMillis());
                int a=random1.nextInt(2);
                Double c=Double.valueOf(values[0].toString());
                double b = random1.nextInt((int)(c*Double.valueOf(values[1].toString())*10000))/1000000.0;
                if(a==1){
                    return c+b;
                }else{
                    return c-b;
                }
            }
        };
        this.addFun(random);

        Function predate= new CommonFunction() {
            public String getName() {
                return "PREDATE";
            }
            @Override
            public Object call(Object... values) {
                SimpleDateFormat format=null;
                Calendar calendar=Calendar.getInstance();
                calendar.setFirstDayOfWeek(2);
                if(values==null){
                    calendar.add(Calendar.DAY_OF_YEAR, -1);
                    format=new SimpleDateFormat("yyyy-MM-dd");
                } else if(values[0].toString().equals("1")){
                    calendar.add(Calendar.DAY_OF_YEAR, -1);
                    format=new SimpleDateFormat("yyyy-MM-dd");
                } else if(values[0].toString().equals("2")){
                    calendar.add(Calendar.MONTH, -1);
                    format=new SimpleDateFormat("yyyy-MM");
                } else if(values[0].toString().equals("3")){
                    calendar.add(Calendar.YEAR, -1);
                    format=new SimpleDateFormat("yyyy");
                }
                return format.format(calendar.getTime());
            }
        };
        this.addFun(predate);

        Function pre15time= new CommonFunction() {
            public String getName() {
                return "PRE15TIME";
            }
            @Override
            public Object call(Object... values) {
                SimpleDateFormat format=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                Calendar calendar=Calendar.getInstance();
                if(values!=null){
                    calendar.setTime(new Date((Long)values[0]));
                }
                calendar.setFirstDayOfWeek(2);
                int minu=calendar.get(Calendar.MINUTE);
                if(minu%15!=0){
                    minu=((int)(minu/15))*15;
                }
                calendar.set(Calendar.SECOND, 0);
                calendar.set(Calendar.MINUTE, minu);
                return format.format(calendar.getTime());
            }
        };
        this.addFun(pre15time);
    }

    public FelNode parse(String var1) {
        return this.parser.parse(var1);
    }

    public Object eval(String var1) {
        return this.eval(var1, this.context);
    }

    public Object eval(String var1, Var... var2) {
        FelNode var3 = this.parse(var1);
        VarVisitOpti var4 = new VarVisitOpti(var2);
        var3 = var4.call(this.context, var3);
        return var3.eval(this.context);
    }

    public Object eval(String var1, FelContext var2) {
        return this.parse(var1).eval(var2);
    }

    public Expression compile(String var1, Var... var2) {
        return this.compile(var1, (FelContext)null, new VarVisitOpti(var2));
    }

    public Expression compile(String var1, FelContext var2, Optimizer... var3) {
        if (var2 == null) {
            var2 = this.context;
        }

        FelNode var4 = this.parse(var1);
        if (var3 != null) {
            Optimizer[] var5 = var3;
            int var6 = var3.length;

            for(int var7 = 0; var7 < var6; ++var7) {
                Optimizer var8 = var5[var7];
                if (var8 != null) {
                    var4 = var8.call(var2, var4);
                }
            }
        }

        return this.compiler.compile(var2, var4, var1);
    }

    public String toString() {
        return "FelEngine";
    }

    public void addFun(Function var1) {
        this.funMgr.add(var1);
    }

    public FelContext getContext() {
        return this.context;
    }

    public CompileService getCompiler() {
        return this.compiler;
    }

    public void setCompiler(CompileService var1) {
        this.compiler = var1;
    }

    public Parser getParser() {
        return this.parser;
    }

    public void setParser(Parser var1) {
        this.parser = var1;
    }

    public FunMgr getFunMgr() {
        return this.funMgr;
    }

    public void setFunMgr(FunMgr var1) {
        this.funMgr = var1;
    }

    public void setContext(FelContext var1) {
        this.context = var1;
    }
}

 

posted @ 2020-06-03 15:40  余生大大  阅读(81)  评论(1编辑  收藏  举报