【微服务】java 规则引擎使用详解
目录
一、什么是规则引擎
1.1 规则引擎概述
1.2 规则引擎执行过程
二、为什么要使用规则引擎
2.1 使用规则引擎的好处
2.1.1 易于维护和更新
2.1.2 增强应用程序的准确性和效率
2.1.3 加快应用程序的开发和部署
2.1.4 支持可视化和可管理性
2.2 规则引擎使用场景
三、常用规则引擎介绍
3.1 Drools
3.1.1 Drools简介
3.1.2 Drools特点
3.1.3 Drools执行流程
3.2 EasyRules
3.2.1 EasyRules功能特性
3.3 uRules
3.3.1 URules特点
3.3.2 URules优势
3.3.2.1 功能强大
3.3.2.2 使用简单
3.3.2.3 性能优异
3.3.2.4 完善的版本控制机制
3.4 jvs-rules
3.4.1 jvs-rules 特点
3.4.2 jvs-rules 核心能力
3.4.2 jvs-rules技术优势
3.5 QLExpress
3.5.1 QLExpress概述
3.5.2 QLExpress特点
四、Drools使用
4.1 案例操作步骤
4.1.1 maven引入核心依赖
4.1.2 增加规则配置文件
4.1.3 定义Drools配置类
4.1.4 业务实现类
4.1.5 业务接口
4.1.6 效果测试
4.2 Drools语法规则说明
4.2.1 基本语法规则
4.2.2 Drools规则文件完整内容
4.2.3 Drools注释说明
4.2.4 Pattern模式匹配
4.2.5 dialect 属性
4.2.6 比较操作符
五、QLExpress使用
5.1 QLExpress 运行原理
5.2 QLExpress 运算符
5.2.1 引入依赖
5.2.2 入门案例
5.2.3 QLExpress常用运算符
5.3 QLExpress API使用
5.3.1 自定义表达式
5.3.2 集合操作
5.3.3 对象操作
5.3.4 函数操作
5.3.5 预定义变量
5.3.6 绑定java方法
5.3.7 自定义操作符
六、写在文末
一、什么是规则引擎
1.1 规则引擎概述
规则引擎,全称业务规则管理系统,规则引擎主要思想是将应用程序中的业务决策部分剥离出来,并使用预定义的语言模块编写业务决策(业务规则),由用户或开发者在需要时进行配置、管理和使用。
规则引擎是一种嵌入在应用程序中的组件,具体使用时接受外部数据输入,解释业务规则,并根据业务规则做出决策,具体来说:
- 规则引擎是从“频繁且通用”的业务变化中抽象出来的中间服务层,实现将决策逻辑从应用代码中分离出来,并使用预定义的高级语法或者可视化的工具编写业务规则并自动优化执行;
- 规则引擎具体执行可以分为接受数据输入,高效解释业务规则,根据业务规则输出决策结果几个过程;
注意:规则引擎并不是是一个具体的实现框架,而是指的是一类系统,即业务规则管理系统,市面上有很多规则引擎的产品,简单来说,规则引擎就是一个输入输出的平台。
1.2 规则引擎执行过程
规则引擎的核心执行流程如下:
- 定义业务规则,即按照规则引擎支持的语法格式定义计算规则或策略;
- 前后端约定业务交互的参数规范,以便服务端能够解析并组装出正确的计算规则;
- 将组装好的符合规则引擎执行规范的参数传入引擎,引擎得到执行结果,为后续其他的业务使用;
二、为什么要使用规则引擎
规则引擎将复杂的业务逻辑从业务代码中剥离出来,可以显著降低业务逻辑实现难度,同时,剥离的业务规则使用规则引擎实现,这样可以使多变的业务规则变得可维护,配合规则引擎提供的良好的业务规则设计器,不用编码就可以快速实现复杂的业务规则。同样,即使是完全不懂编程的业务人员,也可以轻松上手使用规则引擎来定义复杂的业务规则。具体来说:
- 业务规则与系统规则分离,可以实现业务规则的集中管理;
- 在不重启系统的情况下可以随时对业务规则进行扩展和维护;
- 可以动态修改业务规则,从而响应业务需求的快速变更;
- 规则引擎相对独立,只需要关心规则,使得业务人员也可以参与编辑、维护系统的业务规则;
- 减少了硬编码业务规则带来的成本和风险;
- 使用规则引擎提供的规则编辑工具,使复杂的业务规则实现变得更简单,从而适应更多的使用场景;
2.1 使用规则引擎的好处
2.1.1 易于维护和更新
规则引擎可使业务规则与系统代码分离,从而降低维护和更新的难度。通过更新规则库或配置文件中的规则,可以快速地修改业务逻辑和规则变化。像下面这样的代码随处可见,如果能使用规则引擎统一配置管理,就可以避免很多冗余而臃肿的业务逻辑。
2.1.2 增强应用程序的准确性和效率
规则引擎能够处理复杂和动态的规则,可以有效地提高应用程序的准确性和效率。通过使用规则引擎,可以帮助用户快速解决复杂的业务问题和数据分析。
类似于种类繁多计算场景,如果使用编程去处理,很容易遗漏或出错,但规则引擎中封装的规则、算法则经历了长期的实践考研,具备充分的可靠性和准确性
2.1.3 加快应用程序的开发和部署
规则引擎可以提高开发效率和开发质量,降低测试和维护成本,从而提高企业效率和效益。
2.1.4 支持可视化和可管理性
规则引擎可以通过图形用户界面和数据可替代性,可以更好地管理规则库和规则的版本控制。
像Drools,uRules等规则引擎工具都提供了可视化的规则配置管理控制台
2.2 规则引擎使用场景
规则引擎常用的应用场景如下:
- 风险控制系统,规则引擎通常是风控系统的核心,使得产品和研发人员你可以不断调整和优化对抗策略,以实现最好的黑灰产识别效果,比如风险贷款、风险评估;
- 数据分析和清洗,数据引擎可以很方便实现对数据的整理、清洗和转换,可以根据不同的需求来自定义数据处理规则,提升数据处理效率;
- 活动促销运营,比如各大电商的优惠策略,满减、打折、加折扣,可以通过规则引擎预制各种促销的活动规则;
三、常用规则引擎介绍
发展到今天,市面上出现了很多成熟的规则引擎解决方案,下面选择几种常用的规则引擎进行说明。
3.1 Drools
3.1.1 Drools简介
drools是一款由JBoss组织提供的基于java语言开发的开源规则引擎,可以将复杂且多变的业务规则从硬编码中解放出来,以规则脚本的形式存放在文件或特定的存储介质中(如存放在数据库中),使得业务规则的变更不需要修改项目代码、重启服务器就可以在线上环境立即生效。
drools官网:https://www.drools.org/
drools中文网:Drools中文网 | 基于java的功能强大的开源规则引擎
drools源码下载地址:https://github.com/kiegroup/drools
3.1.2 Drools特点
Drools具有如下特点:
- 将规则定义和管理从应用程序代码中分离出来,使得规则可以独立于应用程序运行;
- 提供基于规则的访问和操作数据的功能,例如过滤、排序、检索等;
- 支持动态规则扩展和维护,可以根据需要添加、删除或修改规则;
- 规则引擎是相对独立的,只关心业务规则,使得业务分析人员也可以参与编辑、维护系统的业务规则;
- 使用规则引擎提供的规则编辑工具,使复杂的业务规则实现变得的简单,减少了硬编码业务规则的成本和风险。
Drools 使用 ReteOO算法执行规则。支持使用自然语言表达业务逻辑,也可以使用 Java/Groovy/Python + XML 语法编写规则。
早期的版本一般由开发人员通过开发工具插件来定义规则,目前已有Drools Workbench通过web服务提供给业务人员维护规则。
3.1.3 Drools执行流程
如下图是Drools执行规则时的流程图
Drools执行时主要的业务流程和内部的组件如上图所示,Drools规则引擎基于以下抽象组件实现:
- 规则(Rules):业务规则或DMN决策。所有规则必须至少包含触发该规则的条件以及对应的操作。
- 事实(Facts):输入到规则引擎的数据,用于规则的条件的匹配。
- 生产内存(Production memory):规则引擎中规则存储的地方。
- 工作内存(Working memory):规则引擎中Fact对象存储的地方。
- 议程(Agenda):用于存储被激活的规则的分类和排序的地方。
当用户或系统在Drools中添加或更新规则相关的信息时,该信息会以一个或多个事实的形式插入Drools规则引擎的工作内存中。Drools规则引擎匹配事实和存储在生产内存中规则,筛选符合执行条件的规则。对于满足条件的规则,规则引擎会在议程中激活和注册对应的规则,在议程中Drools会进行优先级的排序和冲突的解决,准备规则的执行。
3.2 EasyRules
asyRules是一款基于Java的开源的轻量级的规则引擎框架,可以帮助开发人员快速开发并管理规则,实现应用程序的自动化决策。EasyRules框架非常易于使用,且可以与任何Java应用程序无缝集成,可以实现复杂的规则表达式匹配。EasyRules是一个基于规则的引擎,它基于规则引擎的常见原则和概念。以下是一些EasyRules框架中的重要概念:
- 规则(Rule):规则是EasyRules框架中的核心概念,它用于描述应用程序中需要遵循的规则。每个规则通常包含两个部分:规则名称和规则条件。
- 规则条件(Condition):规则条件定义了规则的前提条件。如果规则条件为true,则规则将被触发执行。否则,规则将被忽略。
- 规则动作(Action):规则动作是在规则被触发时执行的一段代码。它可以用于实现各种应用程序逻辑,例如更新数据、发送消息等。
- 规则执行(Rule Engine):规则执行是EasyRules框架的核心功能之一,它负责解析规则条件,并根据条件执行相应的规则动作。
3.2.1 EasyRules功能特性
Easy Rules是一个简单但功能强大的Java规则引擎,提供以下特性:
- 轻量级框架和易于学习的API;
- 基于POJO的开发;
- 支持从原始规则创建组合规则;
- 支持通过表达式(如MVEL,SPEL和JEXL)定义规则;
3.3 uRules
URule Pro是一款国产化的自主研发的一款纯Java规则引擎,可以运行在Windows、Linux、Unix等各种类型的操作系统之上; URule Pro的规则设计器采用业内首创的纯浏览器编辑模式,无须安装任何工具,打开浏览器即可完成复杂规则的设计与测试。
uruls学习文档:1.简介 · URule规则引擎使用指南
3.3.1 URules特点
URules具有如下特点:
- 将基于业务规则决策的业务,从手工编码中分离,使用预定义的语义进行业务规则可视化建模,然后接受数据输入,根据定义好的业务规则进行运算,得到输出并做出业务决策;
- 基于浏览器的可视化规则设计器;
- 8种建模工具 ,15大类和40小类科学计算公式;
- 基于浏览器的仿真测试机制;
- 完善的版本控制机制;
- 规则库在线导入、导出,热部署的支持;
- 基于多线程的并行批处理的支持;
- 4种部署模式,兼容各种系统架构;
uRules技术架构图
3.3.2 URules优势
在URule Pro当中,提供规则集、决策表、交叉决策表(决策矩阵)、决策树、评分卡、复杂评分卡、规则流等八种类型的业务规则设计工具,从各个角度满足复杂业务规则设计的需要。
3.3.2.1 功能强大
在URule Pro当中,提供规则集、决策表、交叉决策表(决策矩阵)、决策树、评分卡、复杂评分卡、规则流等八种类型的业务规则设计工具,从各个角度满足复杂业务规则设计的需要。
3.3.2.2 使用简单
URule Pro中提供的所有的规则设计器及打包测试工具,全部基于浏览器实现,所有的规则设计器皆为可视化、图形化设计器,通过鼠标点击即可实现复杂的业务规则定义,URule Pro中规则的多条件组合也是以图形方式展现,这样即使没有任何编程经验的普通业务人员,也可以轻松上手,完成复杂业务规则的定义。
因为所有的业务规则设计器都是基于网页的,且规则的定义都是通过鼠标点击的方式完成,所以对于一个普通的使用者来说,配合教学视频两到三天即可完全掌握URule Pro中各种设计器的使用,结合业务需要定义出想要的业务规则。
3.3.2.3 性能优异
URule Pro后台采用纯Java实现,运行时借鉴Rete了算法的优势,再结合中式规则引擎的特点,独创了一套自己的规则模式匹配算法,这套算法可以从根本上保证规则运行的效率,实现大量复杂业务规则计算时的毫秒级响应。
3.3.2.4 完善的版本控制机制
在URule Pro当中,无论是单个规则文件、或是用户调用的规则包,都提供了完善的版本控制机制。对于规则文件来说只要有需要,可以回退到任何一个历史版本; 对于给用户调用的规则包,可以在不同的历史版本之间灵活切换。
3.4 jvs-rules
JVS-RULES规则引擎是一款可视化的业务规则设计器,它的核心功能在于可集成多种数据源,包括多种类型数据库和API,将数据进行界面可视化的加工处理后,形成策略节点所需要的各种变量,然后通过规则的可视化编排,形成各种决策场景,让业务系统可以快速简单的调用,在线实时获得业务决策结果。
gitee开源地址:https://gitee.com/software-minister/jvs-rules
在线体验地址:http://rules.bctools.cn
3.4.1 jvs-rules 特点
良好的集成性
JVS-rules可以在界面上快速集成数据,通过可视化的配置无需硬编码,可实现数据库、API的接入。
使用灵活
规则引擎使用可视化编排工具,允许用户以拖拽方式构建规则,降低使用难度,提高工作效率。
上手简单
提供基于界面的操作方式,并提供大量的操作文档与学习视频,快速了解并掌握产品的使用方法。
3.4.2 jvs-rules 核心能力
数据集成加工
将不同来源的数据进行整合,通过变量界面化对数据进行加工,挖掘数据业务含义
构建决策建模
利用数据变量,结合各种策略模式,构建预测各种判断模型、业务决策
在线决策支持
提供快速、标准的调用模式,企业的业务系统可简单、高效的接入系统,便捷高效的完成在线决策
3.4.2 jvs-rules技术优势
- 性能强大,采用分布式架构,能够处理多业务的高并发、低延迟;
- 扩展性强,支持多种数据源接入,具备良好的横向扩展能力,能满足未来业务增长的需求;
- 高度定制,提供丰富的接口和插件,支持用户进行二次开发和定制,满足特定场景的需求;
- 易于维护,采用可视化编排和友好的用户界面,降低使用难度,提高运维效率;
- 源码开放,采用技术开放的模式,为客户提供技术无后顾之忧;
3.5 QLExpress
3.5.1 QLExpress概述
QLExpress由阿里的电商业务规则、表达式(布尔组合)、特殊数学公式计算(高精度)、语法分析、脚本二次定制等强需求而设计的一门动态脚本引擎解析工具。
- 是一个轻量级的类java语法规则引擎,作为一个嵌入式规则引擎在业务系统中使用;
- 让业务规则定义简便而不失灵活。让业务人员就可以定义业务规则;
- 支持标准的JAVA语法,还可以支持自定义操作符号、操作符号重载、函数定义、宏定义、数据延迟加载等;
git源码地址:https://github.com/tanqiwei/QLExpressionStudy
3.5.2 QLExpress特点
QLExpress具有如下特点:
- 线程安全,引擎运算过程中的产生的临时变量都是threadlocal类型;
- 执行高效,比较耗时的脚本编译过程可以缓存在本地机器,运行时的临时变量创建采用了缓冲池的技术,和groovy性能相当;
- 弱脚本类型语言,和groovy,javascript语法类似,虽然比强类型脚本语言要慢一些,但是使业务的灵活度大大增强;
- 安全控制,可以通过设置相关运行参数,预防死循环、高危系统api调用等情况;
- 代码精简,依赖最小,250k的jar包适合所有java的运行环境,在android系统的低端pos机也得到广泛运用;
四、Drools使用
相信很多有过网购经验的同学对购物时商品打折不陌生,比如某电商网站推出了根据不同等级的会员享受不同的折扣,钻石级7.5折,金卡8.5折,银卡9折,同时为了吸引客户,再叠加其他的优惠策略,购买当前商品的次数达到2次再减10元,达到5次减30元,超过5次减50元....
类似这样的场景还有很多,接下来我们使用Drools来实现一个类似的功能。按照下面的操作步骤执行
4.1 案例操作步骤
4.1.1 maven引入核心依赖
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.4.RELEASE</version></parent><properties><drool.version>7.59.0.Final</drool.version></properties><dependencies><dependency><groupId>org.drools</groupId><artifactId>drools-compiler</artifactId><version>${drool.version}</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-core</artifactId><version>${drool.version}</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-decisiontables</artifactId><version>${drool.version}</version></dependency><dependency><groupId>org.drools</groupId><artifactId>drools-mvel</artifactId><version>${drool.version}</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId></dependency></dependencies>
4.1.2 增加规则配置文件
在resources目录下创建一个文件夹,用于保存Drools的规则配置文件,规则文件名称为,customer-discount.drl,在里面定义对应的规则,如果需要更多的规则继续在后面追加
import com.congge.model.OrderRequest;
import com.congge.model.CustomerType;
global com.congge.model.OrderDiscount orderDiscount;dialect "mvel"// 规则1: 根据年龄判断
rule "Age based discount"when// 当客户年龄在20岁以下或者50岁以上OrderRequest(age < 20 || age > 50)then// 则添加10%的折扣System.out.println("==========Adding 10% discount for Kids/ senior customer=============");orderDiscount.setDiscount(orderDiscount.getDiscount() + 10);
end// 规则2: 根据客户类型的规则
rule "Customer type based discount - Loyal customer"when// 当客户类型是LOYALOrderRequest(customerType.getValue == "LOYAL")then// 则增加5%的折扣System.out.println("==========Adding 5% discount for LOYAL customer=============");orderDiscount.setDiscount(orderDiscount.getDiscount() + 15);
endrule "Customer type based discount - others"whenOrderRequest(customerType.getValue != "LOYAL")
thenSystem.out.println("==========Adding 3% discount for NEW or DISSATISFIED customer=============");orderDiscount.setDiscount(orderDiscount.getDiscount() + 3);
endrule "Amount based discount"whenOrderRequest(amount > 1000L)thenSystem.out.println("==========Adding 5% discount for amount more than 1000$=============");orderDiscount.setDiscount(orderDiscount.getDiscount() + 5);
end
在这个规则配置文件中,定义了3种规则,
- 根据年龄判断,不同的年龄端最终计算出的折扣不同;
- 根据客户类型的规则,不同类型的客户计算出的折扣不同;
- 根据购买的单价规则,不同的单价最终计算出的折扣不同;
4.1.3 定义Drools配置类
该类相当于在spring容器启动加载后全局注册了一个KieContainer的bean,bean注册到容器中时会加载上述的规则配置文件,从而将规则实例化到bean容器,而后我们就可以使用KieContainer中相关的API进行规则的使用。
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieModule;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class DroolsConfig {private static final String RULES_CUSTOMER_RULES_DRL = "ruls/customer-discount.drl";private static final KieServices kieServices = KieServices.Factory.get();@Beanpublic KieContainer kieContainer() {KieFileSystem kieFileSystem = kieServices.newKieFileSystem();kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_CUSTOMER_RULES_DRL));KieBuilder kb = kieServices.newKieBuilder(kieFileSystem);kb.buildAll();KieModule kieModule = kb.getKieModule();KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId());return kieContainer;}
}
4.1.4 业务实现类
在实际业务中,如何将业务中的数据交由Drools去执行呢,一个正常的思路就是,通过接口将外部传入的参数进行解析,然后传给上面的Container,由Container去执行规则即可,请看下面的业务方法
import com.congge.model.OrderDiscount;
import com.congge.model.OrderRequest;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class OrderDiscountService {@Autowiredprivate KieContainer kieContainer;public OrderDiscount getDiscount(OrderRequest orderRequest) {OrderDiscount orderDiscount = new OrderDiscount();// 开启会话KieSession kieSession = kieContainer.newKieSession();// 设置折扣对象kieSession.setGlobal("orderDiscount", orderDiscount);kieSession.insert(orderRequest);// 触发规则,交给规则引擎进行计算kieSession.fireAllRules();//通过规则过滤器实现只执行指定规则//kieSession.fireAllRules(new RuleNameEqualsAgendaFilter("Age based discount"));kieSession.dispose();return orderDiscount;}
}
上述业务类用到的其他类
OrderRequest,请求参数对象,定义了接口传参的相关属性,该类也将传递到自定义的引擎规则配置文件中进行计算
@Data
public class OrderRequest {/*** 客户号*/private String customerNumber;/*** 年龄*/private Integer age;/*** 订单金额*/private Integer amount;/*** 客户类型*/private CustomerType customerType;
}
定义一个客户类型CustomerType 的枚举,规则引擎会根据该值计算客户订单折扣百分比
public enum CustomerType {LOYAL, NEW, DISSATISFIED;public String getValue() {return this.toString();}
}
订单折扣类 OrderDiscount
,用来表示计算得到的最终的折扣
@Data
public class OrderDiscount {private Integer discount = 0;
}
4.1.5 业务接口
自定义一个接口,用于测试计算规则的结果
@RestController
public class OrderDiscountController {@Autowiredprivate OrderDiscountService orderDiscountService;@PostMapping("/get/discount")public OrderDiscount getDiscount(@RequestBody OrderRequest orderRequest) {OrderDiscount discount = orderDiscountService.getDiscount(orderRequest);return discount;}
}
4.1.6 效果测试
使用postman测试一下,可以控制不同的输入参数然后观察输出结果
4.2 Drools语法规则说明
上面通过一个简单的案例快速演示了一下Drools的使用,接下来深入了解下Drools的使用规则
4.2.1 基本语法规则
使用 Drools 时非常重要的一个事情就是编写规则文件,规则文件的后缀为通常为.drl,drl是Drools Rule Language的缩写,然后在规则文件中编写具体的规则内容即可。一个完整的drl中定义的规则体结构如下:
rule "ruleName"attributeswhenLHSthenRHS
end
补充说明:
- rule:关键字,表示规则开始,规则的唯一名称,一般定义时选择具有业务含义的标识命名;
- attributes:可选项,规则属性,是rule与when之间的参数;
- when:关键字,后面紧跟规则条件部分,比如上述条件为价格在什么区间内;
- LHS(Left Hand Side):是规则的条件部分的通用名称,它由零个或多个条件元素组成。如果LHS为空,则它将被视为始终为true的条件元素。还可以定义多个pattern,多个pattern之间可以使用and或者or进行连接,也可以不写,默认连接为and;
- then:关键字,后面跟规则的结果部分;
- RHS(Right Hand Side):是规则的后果或行动部分的通用名称;
- end:关键字,表示一个规则结束;
不难理解,其实Drools规则引擎的语法和我们编写js或Java代码非常相似,上手编写规则文件只要遵循一定的规范,非Java开发人员也可以快速上手。
4.2.2 Drools规则文件完整内容
下面总结了Drools编写规则文件中常用的属性和内容
关键字 | 描述 |
package | 包名,只限于逻辑上的管理,同一个包名下的查询或者函数可以直接调用 |
import | 用于导入类或者静态方法 |
global | 全局变量 |
function | 自定义函数 |
query | 查询 |
rule end | 规则体 |
Drools支持的规则文件,除了drl形式,还有Excel文件类型的。
4.2.3 Drools注释说明
在drl形式的规则文件中使用注释和Java类中使用注释一致,分为单行注释和多行注释。
单行注释用//进行标记,多行注释以/开始,以/结束。如下示例:
//规则rule1的注释,这是一个单行注释
rule "rule1"whenthenSystem.out.println("rule1触发");
end
/*
规则rule2的注释,
这是一个多行注释
*/
rule "rule2"whenthenSystem.out.println("rule2触发");
end
4.2.4 Pattern模式匹配
前面的学习我们已经知道Drools 中的匹配器可以将 Rule Base 中的所有规则与Working Memory中的Fact对象进行模式匹配,那么我们就需要在规则体的LHS部分定义规则并进行模式匹配。LHS部分由一个或者多个条件组成,条件又称为pattern。
pattern语法结构
绑定变量名: Object(Field约束)
其中 绑定变量名可以省略,通常绑定变量名的命名一般建议以 $ 开始。如果定义了绑定变量名,就可以在规则体的 RHS 部分使用此绑定变量名来操作相应的Fact对象。Field约束部分是需要返回true或者false的0个或多个表达式。如下代码示例:
规则1:
//总价在100到200元的优惠20元
rule "rule_discount_1"when//Order为类型约束,originalPrice为属性约束$order:Order(originalPrice < 200 && originalPrice >= 100)then$order.setRealPrice($order.getOriginalPrice() - 20);System.out.println("成功匹配到规则rule_discount_1:商品总价在100到200元的优惠20元");
end
规则2:
//规则二:总价在100到200元的优惠20元
rule "rule_discount_2"when$order:Order($op:originalPrice < 200 && originalPrice >= 100)thenSystem.out.println("$op=" + $op);$order.setRealPrice($order.getOriginalPrice() - 20);System.out.println("rule_discount_2:商品总价在100到200元的优惠20元");
end
LHS部分还可以定义多个pattern,多个pattern之间可以使用and或者or进行连接,也可以不写,默认连接为and,如下示例:
//规则3:总价在100到200元的优惠20元
rule "rule_discount_3"when$order:Order($op:originalPrice < 200 && originalPrice >= 100) and$customer:Customer(age > 20 && gender=='male')thenSystem.out.println("$op=" + $op);$order.setRealPrice($order.getOriginalPrice() - 20);System.out.println("rule_discount_3:商品总价在100到200元的优惠20元");
end
4.2.5 dialect 属性
drools 支持两种 dialect:java 和mvel,可以理解为引擎识别的一种方言规则
- dialect:缺省为 java,当然我们也推荐统一使用java dialect, 以降低维护成本
- dialect:属性仅用于设定 RHS 部分语法,LHS 部分并不受 dialect 的影响
package 和 rule 都可以指定 dialect 属性,mvel 是一种表达式语言, github主页为 git地址 , 文档主页为 :mvl文档,dools 中的 mvel dialect 可以认为是 java dialect 的超集, 也就是说 mvel dialect 模式下, 也支持 java dialect 的写法,mvel 和 java 的主要区别:
1)对于POJO 对象, java dialect 必须使用 getter 和 setter 方法;
2)对于POJO 对象, mvel dialect 可以直接使用属性名称进行读写, 甚至是 private 属性也可以;
如下,我们定义dialect为Java
rule "java_rule" enabled truedialect "java"when$order:Order()thenSystem.out.println("java_rule fired");$order.setRealPrice($order.getOriginalPrice() * 0.8) ;
end
如果使用mvl,如下示例
rule "mvel_rule"enabled falsedialect "mvel"when$order:Order()thenSystem.out.println("mvel_rule fired");$order.realPrice=$order.originalPrice*0.7 ;
end
4.2.6 比较操作符
在编写规则文件时,离不开各种操作符的使用,Drools提供的比较操作符有:>、<、>=、<=、==、!=、contains 、not contains、memberOf 、not memberOf、matches 、not matches
操作符 | 说明 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
== | 等于 |
!= | 不等于 |
contains | 检查一个Fact对象的某个属性值是否包含一个指定的对象值 |
not contains | 检查一个Fact对象的某个属性值是否不包含一个指定的对象值 |
memberOf | 判断一个Fact对象的某个属性是否在一个或多个集合中 |
not memberOf | 判断一个Fact对象的某个属性是否不在一个或多个集合中 |
前面几个比较操作符和我们日常编程中基本类似,关于后面几个操作符在使用上做一下补充
contains | not contains
Object(Field[Collection/Array] contains value)
Object(Field[Collection/Array] not contains value)
memberOf | not memberOf
Object(field memberOf value[Collection/Array])
Object(field not memberOf value[Collection/Array])
matches | not matches
Object(field matches “正则表达式”)
Object(field not matches “正则表达式”)
五、QLExpress使用
从使用经验来说,Drools相对较重,此时你可以考虑使用QLExpress,可以说QLExpress的在使用上与Drools差别不大,同时语法灵活,规则编写容易上手,下面来详细介绍下QLExpress的使用。
官方地址:官方文档
5.1 QLExpress 运行原理
QLExpress整体运行架构如下
针对上图中的执行步骤,做如下补充说明:
- 单词分解;
- 单词分析;
- 构建语法树进行语法分析;
- 生成运行期指令集合;
- 执行生成的指令集合;
其中前4个过程涉及语法的匹配运算等非常耗时,可以设置execute方法的 isCache ,是否使用Cache中的指令集参数,它可以缓存前四个过程。
即把express语句在本地缓存中换成一段指令,第二次重复执行的时候直接执行指令,极大的提高了性能。或者ExpressRunner设置成singleton(结合spring是非常容易做到的)。
5.2 QLExpress 运算符
5.2.1 引入依赖
<dependency><groupId>com.alibaba</groupId><artifactId>QLExpress</artifactId><version>3.2.0</version></dependency>
5.2.2 入门案例
通过下面这个入门案例体验下QLExpress的使用
public static void main(String[] args) throws Exception {ExpressRunner runner = new ExpressRunner();/*** 表达式计算的数据注入接口*/DefaultContext<String, Object> context = new DefaultContext<String, Object>();context.put("a",1);context.put("b",2);context.put("c",3);String express = "a+b*c";//===> 定义规则,计算表达式等等Object r = runner.execute(express, context, null, true, true);// 解析规则+执行规则System.out.println("执行结果:" + r);}
使用QLExpress进行编码时,主要包括3步:
- 定义ExpressRunner ,顾名思义即表达式的执行器;
- DefaultContext,简单理解就是一个存储上下文参数的容器,将需要传入到规则引擎中的计算参数封装到这个context中,后文在真正执行计算的时候取出来使用;
- 调用runner.execute方法,执行规则计算;
5.2.3 QLExpress常用运算符
QLExpress支持普通的Java语法执行,利用这些运算符,可以很好的满足复杂的业务场景下规则的计算,属性java或js语法的对此应该不陌生。下面总结了常用的运算符:
运算符 | 说明 | 示例 |
---|---|---|
+,-,*,/,<,>,<=,>=,==,!=,%,++,– | mod 等同于% | a * b |
in,like,&&,! | in,like类似于sql语法 | a in [1,2,3] |
for,break、continue,if then else | 1、不支持try{}catch{} 2、不支持java8的lambda表达式 3、不支持for循环集合操作 4、弱类型语言,请不要定义类型声明,更不要用Templete 5、array的声明不一样 6、min,max,round,print,println,like,in 都是系统默认函数的关键字,请不要作为变量名 | int n=10; int sum=0; for(int i=0;i<n;i++){ sum=sum+i;} return sum; |
其他运算符补充,比如逻辑运算符,如下:
运算符 | 说明 | 示例 |
---|---|---|
位运算 | ^,~,&,|,<<,>> | 1<<2 |
四则运算 | +,-,*,/,%,++,-- | 3%2 |
Boolean运算 | !,<,>,<=,>=,==,!=,&&,|| | 2==3 |
其他运算 | =,?: | 2>3?1:0 |
部分运算符在编码中的使用说明:
运算符 | 描述 | 运算符 | 描述 |
---|---|---|---|
mod | 与 % 等同 | for | 循环语句控制符 |
return | 进行值返回 | if | 条件语句控制符 |
in | 类似sql语句的in | then | 与if同用 |
exportAlias | 创建别名,并转换为全局别名 | else | 条件语句控制符 |
alias | 创建别名 | break | 退出循环操作符 |
macro | 定义宏 | continue | 继续循环操作符 |
exportDef | 将局部变量转换为全局变量 | function | 进行函数定义 |
import | 引入包或类,需在脚本最前头 | class | 定义类 |
NewMap | 创建Map | NewList | 创建集合 |
like | 类似sql语句的like | new | 创建一个对象 |
5.3 QLExpress API使用
在使用中,就是根据实际的业务场景进行各种运算符、函数等组合使用,掌握了这些运算符的使用基本上就搞清了QLExpress 的使用
5.3.1 自定义表达式
自定义表达式在一些复杂的场景中可以非常灵活的进行规则编排,只要遵循基本的编码规范和QLExpress中的运算符规范即可
/*** 语法基本说明:* 不支持try{}catch{}* 不支持java8的lambda表达式* 不支持for循环集合操作for (GRCRouteLineResultDTO item : list)* 弱类型语言,请不要定义类型声明* 不要用Templete(Map<String,List>之类的)* array的声明不一样* min,max,round,print,println,like,in 都是系统默认函数的关键字,请不要作为变量名* @param runner*/private static void basicStatement(ExpressRunner runner) throws Exception {DefaultContext<String, Object> defaultContext = new DefaultContext<>();// defaultContext.put("n",10); //直接从Java中传递上下文等于在表达式中传递上下文String loopStatement = "sum=0;n=10;" +"for(i=0;i<n;i++){\n" +"sum=sum+i;\n" +"}\n" +"return sum;";Object result = runner.execute(loopStatement, defaultContext, null, false, false);System.out.println("loopStatement :" + result);// 注意使用同一个defaultContext,上一步语句执行的中间变量会被传递到下一个语句中String maxmiumStatement = "a=1;\n" +"b=2;\n" +"maxnum = a>b?a:b;";result = runner.execute(maxmiumStatement, defaultContext, null, false, false);System.out.println("计算三元表达式的结果:" + result);}
5.3.2 集合操作
这里主要介绍比较常用的两种集合,map和list的操作
map集合操作方式1:
这种方式的操作需要参考官方文档遵循相关的编码格式规范
/*** 集合操作* @param runner* @throws Exception*/private static void collectionStatement(ExpressRunner runner) throws Exception {DefaultContext<String, Object> context = new DefaultContext<String, Object>();String express = "abc = NewMap(1:1,2:2); return abc.get(1) + abc.get(2);";Object rMap = runner.execute(express, context, null, false, false);System.out.println("map集合操作结果:" + rMap);express = "abc = NewList(1,2,3); return abc.get(1)+abc.get(2)";Object rList = runner.execute(express, context, null, false, false);System.out.println("list集合操作结果:" + rList);express = "abc = [1,2,3]; return abc[0]+abc[2];";Object rArr = runner.execute(express, context, null, false, false);System.out.println("arr集合操作结果 :" + rArr);}
map集合操作方式2:
/*** 集合操作* @param runner* @throws Exception*/private static void collectionStatement2(ExpressRunner runner) throws Exception {DefaultContext<String, Object> defaultContext = new DefaultContext<>();HashMap<String, String> mapData = new HashMap(){{put("a","hello");put("b","world");put("c","!@#$");}};defaultContext.put("map",mapData);//ql不支持for(obj:list){}的语法,只能通过下标访问。String mapTraverseStatement = " keySet = map.keySet();\n" +" objArr = keySet.toArray();\n" +" for (i=0;i<objArr.length;i++) {\n" +" key = objArr[i];\n" +" System.out.println(key + ' : ' +map.get(key));\n" +" }";runner.execute(mapTraverseStatement,defaultContext,null,false,false);}
list集合操作方式:
public static void collectionStatement3(ExpressRunner runner) throws Exception {DefaultContext<String, Object> defaultContext = new DefaultContext<>();List list = Arrays.asList("ng", "si", "umid", "ut", "mac", "imsi", "imei");defaultContext.put("list", list);String listStatement ="for(i=0;i<list.size();i++){\n" +" System.out.println('元素 '+ i + ' :'+ list.get(i));\n" +"}\n";runner.execute(listStatement, defaultContext, null, false, false);}
5.3.3 对象操作
QLExpress引擎执行时也支持对对象参数的解析,参考下面的示例
实际业务开发中这是一种比较常用的方式,因为很多界面操作的交互都需要通过接口传递对象参数
/*** 对Java对象的操作* 系统自动会import java.lang.*,import java.util.*;* @param runner*/private static void objectStatement(ExpressRunner runner) throws Exception {
// TradeEvent tradeEvent = new TradeEvent();
// tradeEvent.setPrice(20.0);
// tradeEvent.setName("购物");
// tradeEvent.setId(UUID.randomUUID().toString());//
//String objectStatement = "import com.congge.expression.TradeEvent;\n" +" tradeEvent = new TradeEvent();\n" +" tradeEvent.setPrice(20.0);\n" +" tradeEvent.id=UUID.randomUUID().toString();\n" +" System.out.println(tradeEvent.getId());\n" +" System.out.println(tradeEvent.price);";runner.execute(objectStatement, new DefaultContext<>(), null, false, false);}
5.3.4 函数操作
也可以自定义函数,然后再在execute方法中调用,函数的方式可以说是QLExpress的一大特色,因为不管是后端开发人员还是前端人员,或者对代码稍有了解的同学都可以编写函数使用
/*** 自定义在QLexpress中的函数* 一般语句的最后一句话是返回结果** @param runner*/private static void functionStatement(ExpressRunner runner) throws Exception {String functionStatement = "function add(int a,int b){\n" +" return a+b;\n" +"};\n" +"\n" +"function sub(int a,int b){\n" +" return a - b;\n" +"};\n" +"\n" +"a=10;\n" +"add(a,4) + sub(a,9);";Object result = runner.execute(functionStatement, new DefaultContext<>(), null, false, false);
// runner.execute(functionStatement, new DefaultContext<>(), null, true, false, 1000);System.out.println("执行结果:" + result);}
5.3.5 预定义变量
预定义变量的方式,即可以直接在执行规则之前,将规则初始化到执行器中,对于那些比较简单的执行规则,使用预定义变量的方式还是值得推荐的
/*** Macro定义, 即预先定义一些内容,在使用的时候直接替换Macro中的变量为上下文的内容* @param runner*/private static void macronStatement(ExpressRunner runner) throws Exception {runner.addMacro("计算平均成绩", "(语文+数学+英语)/3.0");runner.addMacro("是否优秀", "计算平均成绩>90");IExpressContext<String, Object> context =new DefaultContext<String, Object>();context.put("语文", 88);context.put("数学", 99);context.put("英语", 95);Object result = runner.execute("是否优秀", context, null, false, false);System.out.println("执行结果:" + result);}
5.3.6 绑定java方法
QLExpress引擎支持将java中的一些内置方法绑定到执行器上
/*** 将Java中已经写好的一些方法,绑定到我们自定义的变量上,在业务中最常用的部分。* @param runner*/private static void workWithJavaStatement(ExpressRunner runner) throws Exception {// 在使用的时候会创建对象runner.addFunctionOfClassMethod("取绝对值", Math.class.getName(), "abs",new String[] { "double" }, null);// 对象已经存在,直接调用对象中的方法runner.addFunctionOfServiceMethod("打印", System.out, "println",new String[] { "String" }, null);String exp = "a=取绝对值(-100);打印(\"Hello World\");打印(a.toString())";DefaultContext<String, Object> context = new DefaultContext<>();runner.execute(exp, context,null,false,false);System.out.println(context);}
5.3.7 自定义操作符
也可以自定义一些操作符,与QLExpress执行器中支持的表达式进行映射之后配合使用,一般使用场景很少
/*** 操作符处理,一般不太常用,但可以自定义一些操作符* @param runner*/private static void extendOperatorStatement(ExpressRunner runner) throws Exception {runner.addOperatorWithAlias("如果", "if",null);runner.addOperatorWithAlias("则", "then",null);runner.addOperatorWithAlias("否则", "else",null);IExpressContext<String, Object> context =new DefaultContext<String, Object>();context.put("语文", 88);context.put("数学", 99);context.put("英语", 95);String exp = "如果 (语文+数学+英语>270) 则 {return 1;} 否则 {return 0;}";
// DefaultContext<String, Object> context = new DefaultContext<String, Object>();Object result = runner.execute(exp, context, null, false, false, null);System.out.println(result);}
更多的场景可以结合官方文档对照学习研究
六、写在文末
本文通过较大的篇幅详细总结了常用的规则引擎的使用,最后介绍了Drools与QLExpress的详细使用,通过对比,可以在实际开发中进行技术选型时提供一个参考,本篇到此结束,感谢观看。
相关文章:

【微服务】java 规则引擎使用详解
目录 一、什么是规则引擎 1.1 规则引擎概述 1.2 规则引擎执行过程 二、为什么要使用规则引擎 2.1 使用规则引擎的好处 2.1.1 易于维护和更新 2.1.2 增强应用程序的准确性和效率 2.1.3 加快应用程序的开发和部署 2.1.4 支持可视化和可管理性 2.2 规则引擎使用场景 三、…...

HCIA-Datacom跟官方路线学习
通过两次更换策略。最后找到最终的学习方案,华为ICT官网有对这个路线的学习,hcia基础有这个学习路线,hcip也有目录路线。所以,最后制定学习路线,是根据这个认证的路线进行学习了: 官网课程:课程…...

MySQL三大日志详细总结(redo log undo log binlog)
MySQL日志 包括事务日志(redolog undolog)慢查询日志,通用查询日志,二进制日志(binlog) 最为重要的就是binlog(归档日志)事务日志redolog(重做日志)undolog…...

XXL-Job详解(二):安装部署
目录 前言环境下载项目调度中心部署执行器部署 前言 看该文章之前,最好看一下之前的文章,比较方便我们理解 XXL-Job详解(一):组件架构 环境 Maven3 Jdk1.8 Mysql5.7 下载项目 源码仓库地址链接: https://github.…...

支持Arm CCA的TF-A威胁模型
目录 一、简介 二、评估目标 2.1 假定 2.2 数据流图 三、威胁分析 3.1 威胁评估 3.1.1 针对所有固件镜像的一般威胁 3.1.2 引导固件可以缓解的威胁...

【Web端CAD/CAE文字标注】webgl+canvas 2d实现文字标注功能
一、需求背景 在CAD/CAE领域经常会遇到显示节点编号这种需求,效果如下图: 本文介绍如何在WebGL中实现文字的显示,对于如何在OpenGL中实现请绕路。 二、实现原理 Canvas是HTML5提供的元素,用于在网页上绘制图形,其支…...

对话框、内部控件位置
一、了解下几个函数 1、movewindow 了解下:MoveWindow 自己塞进去的是屏幕坐标 CrvtFaultRodDlg* dlg new CrvtFaultRodDlg();if (dlg ! NULL){BOOL ret dlg->Create(IDD_DlgCrvtFaultRod, NULL);if (ret) //Create failed.{RECT rect;{RECT rect1;dlg->…...

【GraphQL 】将GraphQL API添加到Postgres数据库的六种简单方法,比较Hasura、Prisma和其他
PostgreSQL是世界上最流行的开源SQL数据库之一,GraphQL是一种日益流行的API规范。 将经过验证和众所周知的PostgreSQL与GraphQL带来的API创建新方式集成在一起不是很好吗? 在本文中,我们讨论了六个不同的项目,它们试图将SQL与Gr…...

每日一题(LeetCode)----哈希表--有效的字母异位词
每日一题(LeetCode)----哈希表–有效的字母异位词 1.题目(242. 有效的字母异位词) 给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互…...

【设计模式】行为型模式-第 3 章第 6 讲【中介者模式】
目录 定义 场景描叙 目的 主要解决 实现 基本类图 案例代码...

Django 通过 Trunc(kind) 和 Extract(lookup_name) 参数进行潜在 SQL 注入 (CVE-2022-34265)
漏洞描述 Django 于 2022 年6月4 日发布了一个安全更新,修复了 Trunc() 和 Extract() 数据库函数中的 SQL 注入漏洞。 参考链接: Django security releases issued: 4.0.6 and 3.2.14 | Weblog | Djang…...

Vue3-toRef 和 toRefs 函数
Vue3-toRef 和 toRefs 函数 功能:可以简化语法调用。toRef 函数执行时会生成一个对象 ObjectRefImpl ,是一个引用对象,具有value属性(getter 和 setter 属性)语法格式:toRef(对象名, 对象中的属性名) toRe…...

STM32---时钟树
写在前面:一个 MCU 越复杂,时钟系统也会相应地变得复杂,如 STM32F1 的时钟系统比较复杂,不像简单的 51 单片机一个系统时钟就 可以解决一切。对于 STM32F1 系列的芯片,其有多个时钟源,构成了一个庞大的是时…...

【功能测试】软件系统测试报告
1.引言 1.1.目的 本测试报告为 xxx 系统测试报告,本报告目的在于总结测试阶段的测试及测试结果分析,描述系统是否达到需求的目的。 本报告预期参考人员包括测试人员、测试部门经理、开发人员、项目管理人员等。 1.2.参考文档 《xxxx系统需求规格说明…...

CentOS一键安装docker脚本
CentOS安装Docker一键脚本 在CentOS上安装Docker是许多项目中常见的任务之一。为了简化这个过程,你可以使用下面的一键脚本。 #!/bin/bash# 卸载旧版本(如果有) sudo yum remove -y docker \docker-client \docker-client-latest \docker-c…...

PostGIS学习教程八:空间关系
PostGIS学习教程八:空间关系 到目前为止,我们只使用了测量(ST_Area、ST_Length)、序列化(ST_GeomFromText)或者反序列化(ST_AsGML)几何图形(geometry)的空间…...

ESP32-Web-Server编程- 通过文本框向 Web 提交数据
ESP32-Web-Server编程- 通过文本框向 Web 提交数据 概述 前述章节我们通过简单 HTML、AJAX、Websocket、SSE 在网页上显示数据,通过网页上的按钮控制 ESP32 的行为。从本节开始,我们将进一步了解通过网页与 ESP32 进行交互的方法。 实现更复杂的交互功…...

NAT网络地址转换
目录 什么是nat nat 实验如何使用SNAT 和 DNAT 实验环境 内网连接外网 1.给网关服务器添加网卡(两张网卡) 2.查看新添加的网卡名 编辑网卡配置 3.开启路由转发 4.打开内网服务器 5.切换到外网服务器(192.168.17.30࿰…...

PyTorch模型训练过程内存泄漏问题解决
近日,在模型训练过程中,发现过一段时间后进程会被kill,观察发现是由于内存泄漏问题造成的。通过逐行代码注释,发现问题在于数据集中的此行代码: info self.data_list[index]这里,self.data_list是dataset…...

【matlab程序】matlab利用工具包nctool读取grib2、nc、opendaf、hdf5、hdf4等格式数据
【matlab程序】matlab利用工具包nctool读取grib2、nc、opendaf、hdf5、hdf4等格式数据 引用: B. Schlining, R. Signell, A. Crosby, nctoolbox (2009), Github repository, https://github.com/nctoolbox/nctoolbox Brief summary: nctoolbox is a Matlab toolbox…...

pytorch训练模板
来源:http://worthpen.top/#/home/blog?blogpot-blog36.md 引言 本项目实现了基于PyTorch Lightning的神经网络训练和测试管道。项目除了实现PyTorch Lightning的工作流外,还实现了通过任务池在训练过程中添加任务、k折交叉验证、将训练结果保存在.cv…...

代码随想录二刷 |字符串 |反转字符串
代码随想录二刷 |字符串 |反转字符串 题目描述解题思路 & 代码实现 题目描述 344.反转字符串 编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。 不要给另外的数组分配额外的空间,你必须…...

Rust语言入门教程(九) - 结构体
格式及语法 在其他很多编程语言中,有类(Class)的存在,在Rust中,我们没有类(Class)的概念,我们使用结构体(Struct)。 与一个结构体相关的有以下几个部分: 数据字段方法关联函数 声明一个结构体及其字段的格式如下&am…...

如何使用Qchan搭建更好保护个人隐私的本地图床并在公网可访问
文章目录 前言1. Qchan网站搭建1.1 Qchan下载和安装1.2 Qchan网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar云端设置2.2 Cpolar本地设置 3. 公网访问测试总结 前言 图床作为云存储的一项重要应用场景,在大量开发人员的努力下,已经开发出大…...

AI伪原创软件-AI伪原创工具下载
在当今数字化时代,创作者们在追求独特创意的同时,也面临着时间和灵感的双重挑战。AI伪原创技术应运而生,为创作者提供了一种快捷而便利的解决方案。本文将专心分享两款备受瞩目的AI伪原创工具,147SEO伪原创、百度文心一言伪原创&a…...

【python脚本】获取OneNET数据写入本地文件
#!/usr/bin/env python # -*- coding: utf-8 -*- # pip install prettytable import time import urllib.request as req import json import os# 设备ID、Key # ESP-12F deviceId "1047311396" APIKey "z0Yq8d3P16l2SbEwuZcXZuCidM"# 上传函数 def OneN…...

5 存储器映射和寄存器
文章目录 5.3 芯片内核5.3.1 ICache5.3.2 DCache5.3.3 FlexRAM 5.4 存储器映射5.4.1 存储器功能划分5.4.1.1 存储器 Block0 内部区域功能划分5.4.1.2 储存器 Block1 内部区域功能划分5.4.1.3 储存器 Block2 内部区域功能划分 5.5 寄存器映射5.5.1 GPIO1的输出数据寄存器 5.3 芯…...
决策树学习
1. 背景 DT决策树是一种基本的分类与回归方法,其学习时,利用训练数据,根据损失函数最小化原则建立DT模型。 分类DT主要优点:模型具有可读性,分类速度快。 由DT树的根结点到叶结点的每一条路径构建一条规则&…...

如何在Ubuntu系统上安装Git
简单介绍 Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。Git是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。Git 与常用的版本控制工具CVS,Subversion 等不同,它采用了分布式版…...

Leetcode.974 和可被 K 整除的子数组
题目链接 Leetcode.974 和可被 K 整除的子数组 rating : 1676 题目描述 给定一个整数数组 n u m s nums nums 和一个整数 k k k ,返回其中元素之和可被 k k k 整除的(连续、非空) 子数组 的数目。 子数组 是数组的 连续 部分。 示例 1&…...