规则引擎--函数式编程和and/or操作符的设计

news2025/1/18 8:13:30

目录

    • Java函数编程的一些基础知识
      • BiFunction
      • BinaryOperator
      • stream reduce
    • And, Or操作符
      • and 逻辑 的 Combiner 如下:
      • or 逻辑 的 Combiner 如下:
      • and, or的执行

接上一篇博文:规则引擎–规则逻辑形如“1 & (2 | 3)“的抽象, 重点分析一下And, Or操作符的设计

Java函数编程的一些基础知识

BiFunction

@FunctionalInterface
public interface BiFunction<T, U, R> {

    /**
     * 将apply函数应用到给定的参数上面
     *
     * @param t 函数的第一个参数
     * @param u 函数的第二个参数
     * @return R 函数的结果
     */
    R apply(T t, U u);

    /**
     * 返回一个组合的函数,第一次是将该函数应用到它的输入,接着是将该函数的after应用到
     * 之前的结果上。如果在任一函数评测期间抛出异常,它都会被传递给组合函数的调用者。
     * @param <V> 组合函数和after函数的输出类型
     * @param after 该函数应用将被在当前函数apply后被apply
     * @return 返回一个组合函数,第一次应用该函数,接着应用after函数
     * @throws 当after为null的时候,会抛出NullPointerException异常。
     */
    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

例子代码

public static void main(String[] args) {
   // BiFunction
    BiFunction<Integer, Integer, Integer> biFunc = (x1, x2) -> x1 + x2;
    Integer result = biFunc.apply(2, 3);
    System.out.println(result); // 5

    Function<Integer,String> function = a->"result:"+a;
    String s = biFunc.andThen(function).apply(1,2);
    System.out.println(s); // result:3
}

BinaryOperator

@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
    /**
     * 通过比较器Comparator来比较两个元素中较小的一个作为返回值返回。
     * @param <T> 比较器的输入参数的类型
     * @param comparator 用来比较两个值的Comparator
     * @return 通过比较器Comparator来比较两个元素中较小的一个作为返回值返回。
     * @throws 如果参数为NULL,就会抛出NullPointerException异常
     */
    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
    }

    /**
     * 通过比较器Comparator来比较两个元素中较大的一个作为返回值返回。
     * @param <T> 比较器的输入参数的类型
     * @param comparator 用来比较两个值的Comparator
     * @return 通过比较器Comparator来比较两个元素中较小的一个作为返回值返回。
     * @throws 如果参数为NULL,就会抛出NullPointerException异常
     */
    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
        Objects.requireNonNull(comparator);
        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
    }
}

例子代码

public static void main(String[] args) {
    BinaryOperator<Integer> integerBinaryOperator = (x1, x2) -> x1 - x2;
    Integer result = integerBinaryOperator.apply(3, 2);
    System.out.println(result); // 1

    Integer a = BinaryOperator.minBy(Integer::compare).apply(1,2);
    System.out.println(a); // 1

    Integer b = BinaryOperator.maxBy(Integer::compare).apply(1,2);
    System.out.println(b); // 2
}

stream reduce

常见释义
英[rɪˈdjuːs][rɪˈduːs]
v.	使还原; 减少; 缩小(尺寸、数量、价格等); (使)蒸发; 减轻体重; 节食;
[例句]Better insulation of your home will help to reduce heating bills.
增加房子的隔热性能会有助于减少供暖费用。
[其他]	第三人称单数:reduces 现在分词:reducing 过去式:reduced 过去分词:reduced

reduce重载的三个方法

// 一个参数: 主要作用 累加、累减,求取最大值、最小值。
Optional<T> reduce(BinaryOperator<T> accumulator);
 
// 两个参数: 多了一个初始值, 同一个参数的方法
T reduce(T identity, BinaryOperator<T> accumulator);
 
// 三个参数:并行流使用
<U> U reduce(U identity,
             BiFunction<U, ? super T, U> accumulator,
             BinaryOperator<U> combiner);

例子1

public static void main(String[] args) {
   final List<Integer> list = Arrays.asList(1, 2, 5, 3, 4);

   // reduce累加
   BinaryOperator<Integer> binaryOperatorAdd = (x1, x2) -> x1 + x2;
   Optional<Integer> result = list.stream().reduce(binaryOperatorAdd);
   System.out.println(result); // Optional[15]

   // reduce求最大值
   Optional<Integer> resultMax = list.stream().reduce(BinaryOperator.maxBy(Integer::compare));
   System.out.println(resultMax); // Optional[5]

   // reduce带初始化值的累加
   Integer result2 = list.stream().reduce(10, binaryOperatorAdd);
   System.out.println(result2); // 25

}

例子2:

public static void main(String[] args) {
    List<Long> list = new ArrayList<>();
    for(long i = 1L;i<20_000_000L;i++){
        list.add(i);
    }
    BinaryOperator<Long> binaryOperatorAdd = (x1, x2) -> x1 + x2;
    long sta = System.currentTimeMillis();
    Long result = list.stream().reduce(0L, binaryOperatorAdd, binaryOperatorAdd);
    long cost = System.currentTimeMillis() - sta;
    System.out.println(result + " cost:" + cost);


    long sta2 = System.currentTimeMillis();
    Long result2 = list.parallelStream().reduce(0L, binaryOperatorAdd, binaryOperatorAdd);
    long cost2 = System.currentTimeMillis() - sta2;
    System.out.println(result2 + " cost:" + cost2);

}

某些时候运行的耗时差距如下
在这里插入图片描述

And, Or操作符

在这里插入图片描述

  • AbstractAndOr
public abstract class AbstractAndOr extends AbstractShortcutableEvaluable<Evaluable<EvalResult>, EvalResult> implements
        Operator<Evaluable<EvalResult>, EvalResult> {

    @Override
    protected BinaryOperator<EvalResult> getCombiner(EvalResult s) {
        return (r1, r2) -> {
            if (r1 == s || r2 == s) {
                return s;
            } else if (r1 == EvalResult.Unknown || r2 == EvalResult.Unknown) {
                return EvalResult.Unknown;
            } else if (r1 == EvalResult.Exception || r2 == EvalResult.Exception) {
                return EvalResult.Exception;
            } else {
                return initial();
            }
        };
    }
}
  • AbstractShortcutableEvaluable
public abstract class AbstractShortcutableEvaluable<E extends Evaluable<T>, T> extends
        AbstractOperandsBasedEvaluable<E, T> {

    /**
     * 定义两操作数求值结果的结合逻辑
     *
     * @param shortcut
     * @return
     */
    protected abstract BinaryOperator<T> getCombiner(T shortcut);

    /**
     * 定义短路值
     *
     * @return
     */
    public abstract T shortcut();

    /**
     * 定义初始值
     *
     * @return
     */
    public abstract T initial();


    @Override
    public T eval(EngineExecutionContext context) {
        return getOperands().stream().reduce(initial(), getAccumulator(context, shortcut()),
                getCombiner(shortcut()));
    }

    private BiFunction<T, Evaluable<T>, T> getAccumulator(EngineExecutionContext context, T shortcut) {
        return (r, op) -> r.equals(shortcut) ? r : getCombiner(shortcut).apply(r,
                op.eval(context));
    }
}

and 逻辑 的 Combiner 如下:

  • and操作符
public class And extends AbstractAndOr {

    @Override
    public EvalResult initial() {
        return EvalResult.True;
    }

    @Override
    public EvalResult shortcut() {
        return EvalResult.False;
    }

    @Override
    public String getOperator() {
        return Operator.AND;
    }

    @Override
    public void accept(EvaluableVisitor visitor) {
        visitor.visit(this);
    }
}

and的shortcut是false,初始值是true, 则combiner 逻辑

BinaryOperator<EvalResult> getCombiner(EvalResult s) {
	// s 为 EvalResult.False
  return (r1, r2) -> {
        if (r1 == s || r2 == s) {
            return s;
        } else if (r1 == EvalResult.Unknown || r2 == EvalResult.Unknown) {
            return EvalResult.Unknown;
        } else if (r1 == EvalResult.Exception || r2 == EvalResult.Exception) {
            return EvalResult.Exception;
        } else {
            return EvalResult.True;
        }
    };
}

即只要有一个False就是false, 为执行未知和异常情况单独处理,否则返回true

or 逻辑 的 Combiner 如下:

  • or操作符
public class Or  extends AbstractAndOr {

    @Override
    public EvalResult initial() {
        return EvalResult.False;
    }

    @Override
    public EvalResult shortcut() {
        return EvalResult.True;
    }

    @Override
    public String getOperator() {
        return Operator.OR;
    }

    @Override
    public void accept(EvaluableVisitor visitor) {
        visitor.visit(this);
    }
}

or的shortcut是true,初始值是false, 则combiner 逻辑

BinaryOperator<EvalResult> getCombiner(EvalResult s) {
	// s 为 EvalResult.True
  return (r1, r2) -> {
        if (r1 == s || r2 == s) {
            return s;
        } else if (r1 == EvalResult.Unknown || r2 == EvalResult.Unknown) {
            return EvalResult.Unknown;
        } else if (r1 == EvalResult.Exception || r2 == EvalResult.Exception) {
            return EvalResult.Exception;
        } else {
            return EvalResult.False;
        }
    };
}

即只要有一个True就是true, 为执行未知和异常情况单独处理,否则返回false

and, or的执行

上面解释了and ,or的Combiner都是正确的,再来看其执行

 @Override
public T eval(EngineExecutionContext context) {
   return getOperands().stream().reduce(initial(), getAccumulator(context, shortcut()),
           getCombiner(shortcut()));
}

private BiFunction<T, Evaluable<T>, T> getAccumulator(EngineExecutionContext context, T shortcut) {
   return (r, op) -> r.equals(shortcut) ? r : getCombiner(shortcut).apply(r, op.eval(context));
}

对于and来说,三目运算符号return (r, op) -> r.equals(shortcut) ? r : getCombiner(shortcut).apply(r, op.eval(context));如果第一个结果是false,那么直接返回该结果(因为 r.equals(shortcut) 这句会满足), 即左右边量有一个为false了,不用and了,直接false

对于or来说,三目运算符号return (r, op) -> r.equals(shortcut) ? r : getCombiner(shortcut).apply(r, op.eval(context));如果第一个结果是true,那么直接返回该结果(因为 r.equals(shortcut) 这句会满足), 即左右边量有一个为true了,不用or了,直接true

否则需要执行combiner的逻辑了

所以and, or操作符号执行如下,

getOperands().stream().reduce(
参数1:	EvalResult.True,
参数2: getAccumulator 同	BinaryOperator<EvalResult> getCombiner, 有短路逻辑
参数3:	BinaryOperator<EvalResult> getCombiner
```

如下截图,反映了其中一次and操作符的执行
![在这里插入图片描述](https://img-blog.csdnimg.cn/70f8b3aaa0174366888b11f9585cf597.png)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/678119.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

反射(reflection)详细讲解

反射(reflection) 反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息&#xff08;比如成员变量&#xff0c;构造器&#xff0c;成员方法等等&#xff09;&#xff0c;并能操作对象的属性及方法。反射在设计模式和框架底层都会用到加载完类之后&#xff0c;在堆…

【博弈论笔记】第四章 重复博弈

文章目录 第四章 重复博弈4.1 重复博弈引论4.1.1 重复博弈定义和意义4.1.2 重复博弈的基本概念 4.2 有限次重复博弈4.2.1 两人零和博弈的有限次重复博弈4.2.2 唯一纯策略纳什均衡博弈的有限次重复博弈4.2.3 多个纯策略纳什均衡博弈的有限次重复博弈4.2.4 有限次重复博弈的民间定…

Python学习笔记(3)--字符串定义、拼接、格式化,表达式格式化,数据输入,布尔数据类型,比较运算符

传送门>B站黑马python入门教程 目录 1.字符串定义方式2.字符串拼接3.字符串格式化3.1 常用占位符3.2 格式化时的数字精度控制3.3 快速格式化字符串 4. 表达式格式化5. 数据输入-input语句6.布尔数据类型、比较运算符 1.字符串定义方式 在 python 语法中,字符串有三种定义方式…

Python爬虫 从小白到高手 Urllib

Urllib 1.什么是互联网爬虫&#xff1f; 如果我们把互联网比作一张大的蜘蛛网&#xff0c;那一台计算机上的数据便是蜘蛛网上的一个猎物&#xff0c;而爬虫程序就是一只小蜘蛛&#xff0c;沿着蜘蛛网抓取自己想要的数据 解释1&#xff1a;通过一个程序&#xff0c;根据Url(http…

行为型模式--访问者模式

目录 概述 结构 案例实现 优缺点 优点&#xff1a; 缺点&#xff1a; 使用场景 概述 封装一些作用于某种数据结构中的各元素的操作&#xff0c;它可以在不改变这个数据结构的前提下定义作用于这 些元素的新的操作。 结构 访问者模式包含以下主要角色: 抽象访问者&am…

采用SpringBoot+Tinymce实现文章的在线预览和上传

采用SpringBootTinymce实现文章的在线预览和上传 资源在gitee中 输入信息 预览

canvas详解09-像素操作

到目前为止,我们尚未深入了解 Canvas 画布真实像素的原理,事实上,你可以直接通过 ImageData 对象操纵像素数据,直接读取或将数据数组写入该对象中。稍后我们也将深入了解如何控制图像使其平滑(反锯齿)以及如何从 Canvas 画布中保存图像。 #ImageData 对象 ImageData对象…

el-table渲染二级对象数组

1、序言 项目地址如下&#xff1a;https://gitee.com/liu-wenxin/complexELTable.git 想要渲染这样的数据&#xff1a; el-table官网给的例子都是一级对象数组&#xff0c;如果想要渲染二级对象数组&#xff0c;直接 :table tableData 这样el-table渲染是不成功的&am…

RadEx Pro处理电火花数据操作步骤(下)

RadEx Pro处理电火花数据操作步骤&#xff08;上&#xff09;主要讲述RadEx Pro读取电火花数据&#xff0c;查看数据的质量&#xff0c;以及简单的滤波和振幅纠正。 6、海底拾取&#xff0c;建立流程060 seafloor pick Trace Input加载stack数据集 Trace Header Math&#x…

【人工智能】— 维度灾难、降维、主成分分析PCA、获取旧数据、非线性主成分分析

【人工智能】— 维度灾难、降维、主成分分析PCA、获取旧数据、非线性主成分分析 高维数据与维度灾难维度灾难降维为什么需要降维&#xff1f;PRINCIPLE COMPONENT ANALYSIS主成分的几何图像最小化到直线距离的平方和举例主成分的代数推导优化问题计算主成分&#xff08;Princip…

【Pandas】pandas用法解析(上)

目录 一、生成数据表 1.导入pandas库 2.导入CSV或者xlsx文件 3.用pandas创建数据表 二、数据表信息查看 1.维度查看 2.数据表基本信息&#xff08;维度、列名称、数据格式、所占空间等&#xff09; 3.每一列数据的格式 4.某一列格式 5.空值判断 6.查看某一列空值 7…

黑马程序员前端 Vue3 小兔鲜电商项目——(七)详情页

文章目录 路由配置模板代码配置路由链接跳转 渲染基础数据封装接口渲染数据 热榜区域模板代码封装接口渲染数据 图片预览组件封装小图切换大图显示模版代码绑定事件 放大镜效果图片优化 SKU组件熟悉全局组件统一插件化插件化开发插件注册 路由配置 模板代码 创建 src\views\D…

快速排序-详解附Python代码

排序思路 取一个元素P&#xff08;第一个元素&#xff09;&#xff0c;目标是使得元素P归位&#xff1b;列表被元素P分成了两个部分&#xff0c;左边的比P小&#xff0c;右边的比P大&#xff1b;分别再对左右两个部分的列表重复1&#xff0c;2步骤&#xff0c;递归完成排序 评…

7Z010 引脚功能详解

本文针对7Z010芯片&#xff0c;详细讲解硬件设计需要注意的技术点&#xff0c;可以作为设计和检查时候的参考文件。问了方便实用&#xff0c;按照Bank顺序排列&#xff0c;包含配置Bank、HR Bank、HP Bank、GTX Bank、供电引脚等。 参考文档包括&#xff1a; ds187-XC7Z010-X…

前端编写贪吃蛇游戏-附详细代码

当我们在前端编写贪吃蛇游戏时&#xff0c;可以按照以下步骤进行&#xff1a; 先看截图&#xff1a; 设置游戏板&#xff1a;创建一个HTML元素作为游戏板&#xff0c;可以使用<div>元素&#xff0c;并为其设置合适的样式。 绘制蛇和食物&#xff1a;使用JavaScript代码…

【Linux 基础入门 + Java项目部署】

文章目录 Linux 基础入门1 Linux 简介1.1不同应用领域的主流操作系统1.2 Linux发展历史与 Linux系统版本 2 Linux 安装2.1 安装方式介绍2.2 安装Linux2.3 网卡设置2.4 安装SSH连接工具 Linux和Windows目录结构对比3 Linux 常用命令3.1 Linux命令初体验3.2 文件目录操作命令lscd…

7A50T 引脚功能详解

本文针对7A50T芯片&#xff0c;详细讲解硬件设计需要注意的技术点&#xff0c;可以作为设计和检查时候的参考文件。问了方便实用&#xff0c;按照Bank顺序排列&#xff0c;包含配置Bank、HR Bank、HP Bank、GTP Bank、供电引脚等。 参考文档包括&#xff1a; ds181_Artix_7_D…

day59_layuimini_crud

今日内容 一、Layui Mini 零、 复习昨日 写在前面的前面 项目开发模式 前端后端数据怎么传输?前端发数据到后台 from,a,ajax, 后端发数据到前端 以前是使用servlet技术,将数据存入请求域/会话域,后台跳转页面到前端,前端jsp页面展现数据现在使用前后分离技术,后端将数据封装成…

【消费战略】解读100个食品品牌丨红海缝隙杀出的乳品独角兽 “认养一头牛”!

认养一头牛品牌历程 2014 年 在河北故城建立第一座大型现代化牧场&#xff0c;从澳洲引进6000头荷斯坦奶牛。 2016 年 11月&#xff0c;在杭州正式创立认养一头牛品牌。 2018年 与天猫达成战略合作&#xff0c;开启会员运营时代。 2020年 跻身天猫“双十一”亿元俱乐部…

Python学习笔记(2)--字面量,注释,变量,数据类型,数据类型转换,标识符,运算符

传送门>B站黑马python入门教程 目录 1.字面量2.注释3.变量4.数据类型5.数据类型转换6.标识符7.运算符算术运算符赋值运算符 1.字面量 字面量: 代码中被固定写的值 python常用的6种数据类型为 数字,字符串,列表,元组,集合,字典 目前基础部分学习字符串,整数,浮点数即可 字符…