一、基础api

在 Drools 当中,规则的编译与运行要通过Drools 提供的各种API 来实现,这些API 总体来讲可以分为三类:规则编译、规则收集和规则的执行。完成这些工作的API 主要有KnowledgeBuilderKnowledgeBaseStatefulKnowledgeSessionStatelessKnowledgeSession等,它们起到了对规则文件进行收集、编译、查错、插入fact、设置global、执行规则或规则流等作用。

1.1、KnowledgeBuilder

KnowledgeBuilder 的作用就是用来在业务代码当中收集已经编写好的规则, 然后对这些规则文件进行编译, 最终产生一批编译好的规则包(KnowledgePackage)给其它的应用程序使用。KnowledgeBuilder 在编译规则的时候可以通过其提供的hasErrors()方法得到编译规则过程中发现规则是否有错误,如果有的话通过其提供的getErrors()方法将错误打印出来,以帮助我们找到规则当中的错误信息。创建KnowledgeBuilder 对象使用的是KnowledgeBuilderFactory 的newKnowledgeBuilder方法,实例如下:

import org.kie.api.io.ResourceType;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.definition.KnowledgePackage;
import org.kie.internal.io.ResourceFactory;
import java.util.Collection;public class Test {public static void main(String[] args) {KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();kbuilder.add(ResourceFactory.newClassPathResource("test.drl", Test.class), ResourceType.DRL);Collection<KnowledgePackage> kpackage = kbuilder.getKnowledgePackages();//产生规则包的集合}
}

通过KnowledgeBuilder 编译的规则文件的类型可以有很多种,如.drl 文件、.dslr 文件或一个xls 文件等。产生的规则包可以是具体的规则文件形成的,也可以是规则流(rule flow)文件形成的,在添加规则文件时,需要通过使用 ResourceType 的枚举值来指定规则文件的类型;同时在指定规则文件的时候drools 还提供了一个名为ResourceFactory 的对象,通过该对象可以实现从Classpath、URL、File、ByteArray、Reader 或诸如XLS 的二进制文件里添加载规则。

1.2、KnowledgeBase

KnowledgeBase 是 Drools 提供的用来收集应用当中知识(knowledge)定义的知识库对象,在一个KnowledgeBase 当中可以包含普通的规则(rule)、规则流(rule flow)、函数定义(function)、用户自定义对象(type model)等。KnowledgeBase 本身不包含任何业务数据对象(fact 对象,后面有相应章节着重介绍fact 对象),业务对象都是插入到由KnowledgeBase产生的两种类型的session 对象当中(StatefulKnowledgeSession 和StatelessKnowledgeSession,后面会有对应的章节对这两种类型的对象进行介绍),通过session 对象可以触发规则执行或开始一个规则流执行。创建一个KnowledgeBase 要通过KnowledgeBaseFactory 对象提供的newKnowledgeBase()方法来实现,这其中创建的时候还可以为其指定一个KnowledgeBaseConfiguration 对象,KnowledgeBaseConfiguration 对象是一个用来存放规则引擎运行时相关环境参数定义的配置对象

import org.kie.api.KieBaseConfiguration;
import org.kie.internal.KnowledgeBase;
import org.kie.internal.KnowledgeBaseFactory;public class Test {public static void main(String[] args) {KieBaseConfiguration kieBaseConf = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();kieBaseConf.setProperty("org.drools.sequential", "true");KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(kieBaseConf);}
}

创建一个KnowledgeBaseConfiguration 对象的方法也是使用KnowldegeBaseFactory,使用的是其提供的newKnowledgeBaseConfiguration()方法,该方法创建好的KnowledgeBaseConfiguration 对象默认情况下会加载drools-core-5.0.1.jar 包下META-INF/drools.default.rulebase.conf 文件里的规则运行环境配置信息,加载完成后,我们可以在代码中对这些默认的信息重新赋值,以覆盖加载的默认值,比如这里我们就把org.drools.sequential 的值修改为true,它的默认值为false。除了这种方式创建KnowledgeBaseConfiguration 方法之外,我们还可以为其显示的指定一个Properties 对象,在该对象中设置好需要覆盖默认值的相关属性的值,然后再通过newKnowledgeBaseConfiguration(Properties prop , ClassLoader loader) 方法创建一个KnowledgeBaseConfiguration 对象。该方法方法当中第一个参数就是我们要设置的Properties对象,第二个参数用来设置加载META-INF/drools.default.rulebase.conf 文件的ClassLoader,

因为该文件在ClassPath 下,所以采用的是ClassLoader 方法进行加载,如果不指定这个参数,那么就取默认的ClassLoader 对象, 如果两个参数都为null , 那么就和newKnowledgeBaseConfiguration()方法的作用相同了。示例如下:

import org.kie.api.KieBaseConfiguration;
import org.kie.internal.KnowledgeBase;
import org.kie.internal.KnowledgeBaseFactory;import java.util.Properties;public class Test {public static void main(String[] args) {Properties properties = new Properties();properties.setProperty("org.drools.sequential", "true");KieBaseConfiguration kieBaseConf = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(properties, null);KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(kieBaseConf);}
}

用来设置默认规则运行环境文件drools.default.rulebase.conf 里面所涉及到的具体项内容

drools.maintainTms = <true|false>
drools.assertBehaviour = <identity|equality>
drools.logicalOverride = <discard|preserve>
drools.sequential = <true|false>
drools.sequential.agenda = <sequential|dynamic>
drools.removeIdentities = <true|false>
drools.shareAlphaNodes = <true|false>
drools.shareBetaNodes = <true|false>
drools.alphaNodeHashingThreshold = <1...n>
drools.compositeKeyDepth = <1..3>
drools.indexLeftBetaMemory = <true/false>
drools.indexRightBetaMemory = <true/false>
drools.consequenceExceptionHandler = <qualified class name>
drools.maxThreads = <-1|1..n>
drools.multithreadEvaluation = <true|false>

KnowledgeBase 创建完成之后,接下来就可以将我们前面使用KnowledgeBuilder 生成的KnowledgePackage 的集合添加到KnowledgeBase 当中,以备使用:

import org.kie.api.KieBaseConfiguration;
import org.kie.api.io.ResourceType;
import org.kie.internal.KnowledgeBase;
import org.kie.internal.KnowledgeBaseFactory;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.definition.KnowledgePackage;
import org.kie.internal.io.ResourceFactory;import java.util.Collection;
import java.util.Properties;public class Test {public static void main(String[] args) {KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();kbuilder.add(ResourceFactory.newClassPathResource("test.drl",Test.class), ResourceType.DRL);Collection<KnowledgePackage> kpackage = kbuilder.getKnowledgePackages();KieBaseConfiguration kieBaseConf = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();kieBaseConf.setProperty("org.drools.sequential", "true");KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(kieBaseConf);knowledgeBase.addKnowledgePackages(kpackage);//将KnowledgePackage集合添加到KnowledgeBase当中}
}

1.3、StatefulKnowledgeSessions

StatefulKnowledgeSession 对象是一种最常用的与规则引擎进行交互的方式,它可以与规则引擎建立一个持续的交互通道,在推理计算的过程当中可能会多次触发同一数据集。在用户的代码当中,最后使用完StatefulKnowledgeSession 对象之后,一定要调用其dispose()方法以释放相关内存资源。

StatefulKnowledgeSession 可以接受外部插入(insert)的业务数据——也叫fact,一个fact 对象通常是一个普通的Java 的POJO,一般它们会有若干个属性,每一个属性都会对应getter 和setter 方法,用来对外提供数据的设置与访问。一般来说,在Drools 规则引擎当中,fact 所承担的作用就是将规则当中要用到的业务数据从应用当中传入进来,对于规则当中产生的数据及状态的变化通常不用fact 传出。如果在规则当中需要有数据传出,那么可以通过在StatefulKnowledgeSession 当中设置global 对象来实现,一个global 对象也是一个普通的Java 对象,在向StatefulKnowledgeSession 当中设置global 对象时不用insert 方法而用setGlobal 方法实现。

创建一个StatefulKnowledgeSession 要通过KnowledgeBase 对象来实现,示例如下:

import org.kie.api.KieBaseConfiguration;
import org.kie.api.io.ResourceType;
import org.kie.internal.KnowledgeBase;
import org.kie.internal.KnowledgeBaseFactory;
import org.kie.internal.builder.KnowledgeBuilder;
import org.kie.internal.builder.KnowledgeBuilderFactory;
import org.kie.internal.definition.KnowledgePackage;
import org.kie.internal.io.ResourceFactory;
import org.kie.internal.runtime.StatefulKnowledgeSession;import java.util.Collection;
import java.util.Properties;public class Test {public static void main(String[] args) {KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();kbuilder.add(ResourceFactory.newClassPathResource("test.drl", Test.class), ResourceType.DRL);Collection<KnowledgePackage> kpackage = kbuilder.getKnowledgePackages();KieBaseConfiguration kieBaseConf = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();kieBaseConf.setProperty("org.drools.sequential", "true");KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(kieBaseConf);knowledgeBase.addKnowledgePackages(kpackage);//将KnowledgePackage集合添加到KnowledgeBase当中StatefulKnowledgeSession statefulKSession = knowledgeBase.newStatefulKnowledgeSession();statefulKSession.setGlobal("globalTest", new Object());//设置一个global对象statefulKSession.insert(new Object());//插入一个fact对象statefulKSession.fireAllRules();statefulKSession.dispose();}
}

1.4、StateLessKnowledgeSession

StatelessKnowledgeSession 的作用与StatefulKnowledgeSession 相仿,它们都是用来接收业务数据、执行规则的。事实上,StatelessKnowledgeSession 对StatefulKnowledgeSession 做了包装,使得在使用StatelessKnowledgeSession 对象时不需要再调用dispose()方法释放内存资源了。

因为StatelessKnowledgeSession 本身所具有的一些特性,决定了它的使用有一定的局限性。在使用StatelessKnowledgeSession 时不能进行重复插入fact 的操作、也不能重复的调用fireAllRules()方法来执行所有的规则,对应这些要完成的工作在StatelessKnowledgeSession当中只有execute(…)方法,通过这个方法可以实现插入所有的fact 并且可以同时执行所有的规则或规则流,事实上也就是在执行execute(…)方法的时候就在StatelessKnowledgeSession内部执行了insert()方法、fireAllRules()方法和dispose()方法。示例如下:

StatelessKnowledgeSession statelessKSession=kbase.newStatelessKnowledgeSession();ArrayList list=new ArrayList();
list.add(new Object());
list.add(new Object());statelessKSession.execute(list);

如果我们要插入的fact 就是这个ArrayList 而不是它内部的Element那该怎么做呢?在StatelessKnowledgeSession 当中,还提供了execute(Command cmd)的方法,在该方法中通过CommandFactory 可以创建各种类型的Command,比如前面的需求要直接将这个ArrayList 作为一个fact 插入,那么就可以采用CommandFactory.newInsert(Object obj)来实现:

statelessKSession.execute(CommandFactory.newInsert(list));

如果需要通过StatelessKnowledgeSession 设置global 的话, 可以使用CommandFactory.newSetGlobal(“key”,Object obj)来实现;如果即要插入若干个fact,又要设置相关的global,那么可以将CommandFactory 产生的Command 对象放在一个Collection 当中,然后再通过CommandFactory.newBatchExecution(Collection collection)方法实现.

二、FACT对象

Fact 是指在Drools 规则应用当中,将一个普通的JavaBean 插入到规则的WorkingMemory当中后的对象。规则可以对Fact 对象进行任意的读写操作,当一个JavaBean 插入到WorkingMemory 当中变成Fact 之后,Fact 对象不是对原来的JavaBean 对象进行Clon,而是原来JavaBean 对象的引用。规则在进行计算的时候需要用到应用系统当中的数据,这些数据设置在Fact 对象当中,然后将其插入到规则的WorkingMemory 当中,这样在规则当中就可以通过对Fact 对象数据的读写,从而实现对应用数据的读写操作。一个Fact 对象通常是一个具有getter 和setter 方法的POJO 对象,通过这些getter 和setter 方法可以方便的实现对Fact 对象的读写操作,所以我们可以简单的把Fact 对象理解为规则与应用系统数据交互的桥梁或通道。

当 Fact 对象插入到WorkingMemory 当中后,会与当前WorkingMemory 当中所有的规则进行匹配,同时返回一个FactHandler 对象。FactHandler 对象是插入到WorkingMemory当中Fact 对象的引用句柄,通过FactHandler 对象可以实现对对应的Fact 对象的删除及修改等操作。

在前面介绍StatefulKnowledgeSession 和StatelessKnowledgeSession 两个对象的时候也提到了插入Fact 对象的方法,在StatefulKnowledgeSession 当中直接使用insert 方法就可以将一个Java 对象插入到WokingMemory 当中,如果有多个Fact 需要插入,那么多个调用insert方法即可;对于StatelessKnowledgeSession 对象可利用CommandFactory 实现单个Fact 对象或多个Fact 对象的插入。

三、规则

3.1、规则文件

在 Drools 当中,一个标准的规则文件就是一个以“.drl”结尾的文本文件,由于它是一个标准的文本文件,所以可以通过一些记事本工具对其进行打开、查看和编辑。规则是放在规则文件当中的,一个规则文件可以存放多个规则,除此之外,在规则文件当中还可以存放

用户自定义的函数、数据对象及自定义查询等相关在规则当中可能会用到的一些对象。

常用的有:package package-name、imports、globals、functions、queries、rules

对于一个规则文件而言,首先声明package 是必须的,除package 之外,其它对象在规则文件中的顺序是任意的,也就是说在规则文件当中必须要有一个package 声明,同时package 声明必须要放在规则文件的第一行。规则文件当中的package 和Java 语言当中的package 有相似之处,也有不同之处。在Java当中package 的作用是用来对功能相似或相关的文件放在同一个package 下进行管理,这种package 管理既有物理上Java 文件位置的管理也有逻辑上的文件位置的管理,在Java 当中这种通过package 管理文件要求在文件位置在逻辑上与物理上要保持一致;在Drools 的规则文件当中package 对于规则文件中规则的管理只限于逻辑上的管理,而不管其在物理上的位置如何,这点是规则与Java 文件的package 的区别。对于同一package 下的用户自定义函数、自定义的查询等,不管这些函数与查询是否在同一个规则文件里面,在规则里面是可以直接使用的,这点和Java 的同一package 里的Java类调用是一样的。

3.2、规则语言

一个规则通常包括三个部分:属性部分(attribute)、条件部分(LHS)和结果部分(RHS)。对于一个完整的规则来说,这三个部分都是可选的。

3.3、条件部分

条件部分又被称之为Left Hand Side,简称为LHS,下文当中,如果没有特别指出,那么所说的LHS 均指规则的条件部分,在一个规则当中when 与then 中间的部分就是LHS 部分。在LHS 当中,可以包含0~n 个条件,如果LHS 部分没空的话,那么引擎会自动添加一个eval(true)的条件,由于该条件总是返回true,所以LHS 为空的规则总是返回true。LHS 部分是由一个或多个条件组成,条件又称之为pattern(匹配模式),多个pattern之间用可以使用and 或or 来进行连接,同时还可以使用小括号来确定pattern 的优先级

一个pattern 的语法如下:

[绑定变量名:]Object([field 约束])

对于一个pattern 来说“绑定变量名”是可选的,如果在当前规则的LHS 部分的其它的pattern 要用到这个对象,那么可以通过为该对象设定一个绑定变量名来实现对其引用,对于绑定变量的命名,通常的作法是为其添加一个“$”符号作为前缀,这样可以很好的与Fact的属性区别开来;绑定变量不仅可以用在对象上,也可以用在对象的属性上面,命名方法与对象的命名方法相同;“field 约束”是指当前对象里相关字段的条件限制,示例如下:

如果两个pattern 没有符号连接,在Drools当中在pattern 中没有连接符号,那么就用and 来作为默认连接,所以在该规则的LHS 部分中两个pattern 只有都满足了才会返回true。默认情况下,每行可以用“;”来作为结束符(和Java 的结束一样),当然行尾也可以不加“;”结尾。

3.3.1、约束连接

对于对象内部的多个约束的连接,可以采用“&&”(and)、“||”(or)和“,”(and)来实现,代码清单2-7 中规则的LHS 部分的两个pattern 就里对象内部约束就采用“,”来实现,“&&”(and)、“||”(or)和“,”这三个连接符号如果没有用小括号来显示的定义优先级的话,那么它们的执行顺序是:“&&”(and)、“||”(or)和“,” “&&”优先级最高,表面上看“,”与“&&”具有相同的含义,但是有一点需要注意,“,”与“&&”和“||”不能混合使用,也就是说在有“&&”或“||”出现的LHS 当中,是不可以有“,”连接符出现的,反之亦然。

3.3.2、比较操作符

在Drools5当中共提供了十二种类型的比较操作符,分别是:>、>=、<、<=、= =、!=、contains、not contains、memberof、not memberof、matches、not matches;在这十二种类型的比较操作符当中,前六个是比较常见也是用的比较多的比较操作符,着重对后六种类型的比较操作符进行介绍。

3.3.3、 contains

比较操作符contains 是用来检查一个Fact 对象的某个字段(该字段要是一个Collection或是一个Array 类型的对象)是否包含一个指定的对象。

contains 只能用于对象的某个Collection/Array 类型的字段与另外一个值进行比较,作为比较的值可以是一个静态的值,也可以是一个变量(绑定变量或者是一个global 对象)。

3.3.4、not contains

not contains 作用与contains 作用相反,not contains 是用来判断一个Fact 对象的某个字段(Collection/Array 类型)是不是包含一个指定的对象,和contains 比较符相同,它也只能用在对象的field 当中。

3.3.5、memberOf

memberOf 是用来判断某个Fact 对象的某个字段是否在一个集合(Collection/Array)当中,用法与contains 有些类似,但也有不同,memberOf 的语法如下:Object(fieldName memberOf value[Collection/Array])可以看到memberOf 中集合类型的数据是作为被比较项的,集合类型的数据对象位于memberOf 操作符后面,同时在用memberOf 比较操作符时被比较项一定要是一个变量(绑定变量或者是一个global 对象),而不能是一个静态值。

3.3.6、not memberOf

该操作符与memberOf 作用洽洽相反,是用来判断Fact 对象当中某个字段值是不是中某个集合(Collection/Array)当中,同时被比较的集合对象只能是一个变量(绑定变量或global对象)。

3.3.7、matches

matches 是用来对某个Fact 的字段与标准的Java 正则表达式进行相似匹配,被比较的字符串可以是一个标准的Java 正则表达式,但有一点需要注意,那就是正则表达式字符串当中不用考虑“\”的转义问题。matches 使用语法如下:

Object(fieldName matches “正则表达式”)

示例:

package test
import java.util.List;

rule "rule1"
    when
        $customer:Customer(name matches "李.*");
    then
        System.out.println($customer.getName());
end

示例的规则就像我们展示了matches 的用法,该规则是用来查找所有Customer 对象的name 属性是不是以“李”字开头,如果满足这一条件那么就将该Customer 对象的name 属性打印出来。

3.3.8、not matches

与matches 作用相反,是用来将某个Fact 的字段与一个Java 标准正则表达式进行匹配,看是不是能与正则表达式匹配。not matches 使用语法如下:Object(fieldname not matches “正则表达式”)

3.4、结果部分

结果部分又被称之为Right Hand Side,简称为RHS,在一个规则当中then 后面部分就是RHS,只有在LHS 的所有条件都满足时RHS 部分才会执行。

RHS 部分是规则真正要做事情的部分,可以将因条件满足而要触发的动作写在该部分当中,在RHS 当中可以使用LHS 部分当中定义的绑定变量名、设置的全局变量、或者是直接编写Java 代码(对于要用到的Java 类,需要在规则文件当中用import 将类导入后方能使用,这点和Java 文件的编写规则相同)。

我们知道,在规则当中LHS 就是用来放置条件的,所以在RHS 当中虽然可以直接编写Java 代码,但不建议在代码当中有条件判断,如果需要条件判断,那么请重新考虑将其放在LHS 当中,否则就违背了使用规则的初衷。

在 Drools 当中,在RHS 里面,提供了一些对当前Working Memory 实现快速操作的宏宏函数或对象,比如insert/insertLogical、update 和retract 就可以实现对当前Working Memory

中的Fact 对象进行新增、删除或者是修改;如果您觉得还要使用Drools 当中提供的其它方法,那么您还可以使用另一外宏对象drools,通过该对象可以使用更多的操作当前WorkingMemory 的方法;同时Drools 还提供了一个名为kcontext 的宏对象,使我们可以通过该对象直接访问当前Working Memory 的KnowledgeRuntime。下面我们就来详细讨论一下这些宏函数的用法。

4.4.1、insert

函数insert 的作用与我们在Java 类当中调用StatefulKnowledgeSession 对象的insert 方法的作用相同,都是用来将一个Fact 对象插入到当前的Working Memory 当中。它的基本用法

格式如下:insert(new Object());

一旦调用insert 宏函数,那么Drools 会重新与所有的规则再重新匹配一次,对于没有设置no-loop 属性为true 的规则,如果条件满足,不管其之前是否执行过都会再执行一次,这个特性不仅存在于insert 宏函数上,后面介绍的update、retract 宏函数同样具有该特性,所以在某些情况下因考虑不周调用insert、update 或retract容易发生死循环,这点大家需要注意,示例如下:

4.4.2、update

update 函数意义与其名称一样,用来实现对当前Working Memory 当中的Fact 进行更新,update 宏函数的作用与StatefulSession 对象的update 方法的作用基本相同,都是用来告诉当前的Working Memory 该Fact 对象已经发生了变化。它的用法有两种形式,一种是直接更新一个Fact 对象,另一种为通过指定FactHandle 来更新与指定FactHandle 对应的Fact 对象,

下面我们就来通过两个实例来说明update 的这两种用法。

先来看第一种用法,直接更新一个Fact 对象。第一种用法的格式如下:update($customer);

为了测试在多次调用update 宏函数更新Customer 对象后Working Memory 当中还只存在一个Customer 对象,所以我们还添加了一个名为“query fact count”的query 查询(关于query 查询后面的章节会有详细介绍),该查询的作用是用来检索当中Working Memory 当中有多少个Working Memory 对象,在该示例当中,Customer 对象应该只有一个。

第二种用法的格式如下: update(new FactHandle(),new Object());

从第二种用法格式上可以看出,它可以支持创建一个新的Fact 对象,从而把FactHandle对象指定的Fact 对象替换掉,从而实现对象的全新更新。

4.4.3、retract

和StatefulSession 的retract 方法一样,宏函数retract 也是用来将Working Memory 当中某个Fact 对象从Working Memory 当中删除,下面就通过一个例子来说明retract 宏函数的用法。

retract($customer);

用 retract 宏函数对符合条件的Customer 对象进行了删除,将其从当前的Working Memory当中清除,这样在执行完所有的规则之后,调用名为“query fact count”的query 查询,查询到的结果数量应该是0,编写测试类,验证我们推理的结果。

4.4.4、drools

如果您希望在规则文件里更多的实现对当前的Working Memory 控制,那么可以使用drools 宏对象实现,通过使用drools 宏对象可以实现在规则文件里直接访问WorkingMemory。在前面介绍update 宏函数的时候我们就使用drools 宏对象来访问当前的WorkingMemory,得到一个指定的Fact 对象的FactHandle。同时前面介绍的insert、insertLogical、update 和retract 宏函数的功能皆可以通过使用drools 宏对象来实现。

下表中罗列了drools 宏对象的常用方法。

方法名称

用法格式

含义

getWorkingMemory()

drools.getWorkingMemory()

获取当前的WorkingMemory 对象

halt()

drools.halt()

在当前规则执行完成后,不再执行其它未执行的规则。

getRule()

drools.getRule()

得到当前的规则对象

insert(new Object)

drools.insert(new Object)

向当前的WorkingMemory 当中插入指定的对象,功能与宏函数insert相同。

update(new Object)

drools.update(new Object)

更新当前的WorkingMemory 中指定的对象,功能与宏函数update 相同。

update(FactHandle

Object)

drools.update(FactHandle

Object)

更新当前的WorkingMemory 中指定的对象,功能与宏函数update 相同。

retract(new Object)

drools.retract(new Object)

从当前的WorkingMemory 中删除指定的对象,功能与宏函数retract 相同。

4.5

4.4.5、kcontext

kcontext 也是Drools 提供的一个宏对象, 它的作用主要是用来得到当前的KnowledgeRuntime 对象,KnowledgeRuntime 对象可以实现与引擎的各种交互。

4.4.6、modify

modify 是一个表达式块,它可以快速实现对Fact 对象多个属性进行修改,修改完成后会自动更新到当前的Working Memory 当中。它的基本语法格式如下:

modify(fact-expression){

<修改Fact 属性的表达式>[,<修改Fact 属性的表达式>*]

}

这里有一点需要注意,那就是和insert、update、retract 对Working Memory 的操作一样,一旦使用了modify 块对某个Fact 的属性进行了修改,那么会导致引擎重新检查所有规则是否匹配条件,而不管其之前是否执行过。

4.5、属性部分

规则属性是用来控制规则执行的重要工具,在前面举出的关于规则的例子当中,已经接触了如控制规则执行优先级的salience,和是否允许规则执行一次的no-loop 等。在目前的Drools5 当中,规则的属性共有13 个,它们分别是:activation-group、agenda-group、auto-focus、date-effective、date-expires、dialect、duration、enabled、lock-on-active、no-loop、ruleflow-group、salience、when,这些属性分别适用于不同的场景,下面我们就来分别介绍这些属性的含义及用法。

4.5.1 salience

该属性的作用我们在前面的内容也有涉及,它的作用是用来设置规则执行的优先级,salience 属性的值是一个数字,数字越大执行优先级越高,同时它的值可以是一个负数。默认情况下,规则的ssalience 默认值为0,所以如果我们不手动设置规则的salience 属性,那么它的执行顺序是随机的。

4.5.2 no-loop

在一个规则当中如果条件满足就对Working Memory 当中的某个Fact 对象进行了修改,比如使用update 将其更新到当前的Working Memory 当中,这时引擎会再次检查所有的规则是否满足条件,如果满足会再次执行。

no-loop 属性的作用是用来控制已经执行过的规则在条件再次满足时是否再次执行,前面的示例当中也用到该属性帮我们解决应该当中的问题。no-loop 属性的值是一个布尔型,默认情况下规则的no-loop属性的值为false,如果no-loop 属性值为true,那么就表示该规则只会被引擎检查一次,如果满足条件就执行规则的RHS 部分,如果引擎内部因为对Fact 更新引起引擎再次启动检查规则,那么它会忽略掉所有的no-loop 属性设置为true 的规则。

5.3 date-effective

该属性是用来控制规则只有在到达后才会触发,在规则运行时,引擎会自动拿当前操作系统的时候与date-effective 设置的时间值进ss行比对,只有当系统时间>=date-effective 设置的时间值时,规则才会触发执行,否则执行将不执行。在没有设置该属性的情况下,规则随时可以触发,没有这种限制。date-effective 的值为一个日期型的字符串,默认情况下,date-effective 可接受的日期格式为“dd-MMM-yyyy”,例如2009 年9 月25 日在设置为date-effective 的值时,如果您的操作系统为中文的,那么应该写成“25-Sep-2009”;如果是英文操作系统“25-九月-2009”,示例:

rule "rule1"

date-effective " 25-九月-2009"

when

eval(true);

then

System.out.println("rule1 is execution!");

End

在实际使用的过程当中,如果您不想用这种时间的格式,那么可以在调用的Java 代码中通过使用System.setProperty(String key,String value)方法来修改默认的时间格式,System.setProperty("drools.dateformat","yyyy-MM-dd");这句就是用来修改当前系统默认的时间格式的。在进行这个操作的时候有两个地方需要注意:一是设置的key必须是“drools.dateformat”,值的话遵循标准的Java日期格式;二是修改当前系统默认的时间格式的这句代码必须放在向KnowledgeBuilder里添加规则文件之前,否则将不起作用。

4.5.4 date-expires

该属性的作用与date-effective 属性恰恰相反, date-expires 的作用是用来设置规则的有效期,引擎在执行规则的时候,会检查规则有没有date-expires 属性,如果有的话,那么会将这个属性的值与当前系统时间进行比对,如果大于系统时间,那么规则就执行,否则就不执行。该属性的值同样也是一个日期类型,默认格式也是“dd-MMM-yyyy”,具体用法与date-effective 属性相同。

4.5.5 enabled

enabled 属性比较简单,它是用来定义一个规则是否可用的。该属性的值是一个布尔值,默认该属性的值为true,表示规则是可用的,如果手工为一个规则添加一个enabled 属性,并且设置其enabled 属性值为false,那么引擎就不会执行该规则。

4.5.6 dialect

该属性用来定义规则当中要使用的语言类型,目前Drools5 版本当中支持两种类型的语言:mvel 和java,默认情况下,如果没有手工设置规则的dialect,那么使用的java 语言。

代码演示了如何查看当前规则的dialect 的值。

rule "rule1"

when

eval(true)

then

System.out.println("dialect:"+drools.getRule().getDialect());

End

4.5.7 duration

对于一个规则来说,如果设置了该属性,那么规则将在该属性指定的值之后在另外一个线程里触发。该属性对应的值为一个长整型,单位是毫秒,下示例里的规则rule1 添加了duration 属性,它的值为3000,表示该规则将在3000 毫秒之后在另外一个线程里触发。

rule "rule1"

duration 3000

when

eval(true)

then

System.out.println("rule thread

id:"+Thread.currentThread().getId());

end

4.5.8 lock-on-active

当在规则上使用ruleflow-group 属性或agenda-group 属性的时候,将lock-on-action 属性的值设置为true,可能避免因某些Fact 对象被修改而使已经执行过的规则再次被激活执行。可以看出该属性与no-loop 属性有相似之处,no-loop 属性是为了避免Fact 修改或调用了insert、retract、update 之类而导致规则再次激活执行,这里的lock-on-action 属性也是起这个作用,lock-on-active 是no-loop 的增强版属性,它主要作用在使用ruleflow-group 属性

或agenda-group 属性的时候。lock-on-active 属性默认值为false。

4.5.9 activation-group

该属性的作用是将若干个规则划分成一个组,用一个字符串来给这个组命名,这样在执行的时候,具有相同activation-group 属性的规则中只要有一个会被执行,其它的规则都将不再执行。也就是说,在一组具有相同activation-group 属性的规则当中,只有一个规则会被执行,其它规则都将不会被执行。当然对于具有相同activation-group 属性的规则当中究竟哪一个会先执行,则可以用类似salience 之类属性来实现。示例:

rule "rule1"

activation-group "test"

when

eval(true)

then

System.out.println("rule1 execute");

end

rule "rule 2"

activation-group "test"

when

eval(true)

then

System.out.println("rule2 execute");

End

rule1 和rule2,这两个规则具有相同的activation-group属性,这个属性的值为“test”,前面我们讲过,具有相同activation-group 属性的规则只会有一个被执行,其它规则将会被忽略掉,所以这里的rule1 和rule2 这两个规则因为具体相同名称的activation-group 属性,所以它们只有一个会被执行。

4.5.10 agenda-group

规则的调用与执行是通过StatelessSession 或StatefulSession 来实现的,一般的顺序是创建一个StatelessSession 或StatefulSession,将各种经过编译的规则的package 添加到session当中,接下来将规则当中可能用到的Global 对象和Fact 对象插入到Session 当中,最后调用fireAllRules 方法来触发、执行规则。在没有调用最后一步fireAllRules 方法之前,所有的规则及插入的Fact 对象都存放在一个名叫Agenda 表的对象当中,这个Agenda 表中每一个规

则及与其匹配相关业务数据叫做Activation,在调用fireAllRules 方法后,这些Activation 会依次执行,这些位于Agenda 表中的Activation 的执行顺序在没有设置相关用来控制顺序的属性时(比如salience 属性),它的执行顺序是随机的,不确定的。

Agenda Group 是用来在Agenda 的基础之上,对现在的规则进行再次分组,具体的分组方法可以采用为规则添加agenda-group 属性来实现。

agenda-group 属性的值也是一个字符串,通过这个字符串,可以将规则分为若干个Agenda Group,默认情况下,引擎在调用这些设置了agenda-group 属性的规则的时候需要显示的指定某个Agenda Group 得到Focus(焦点),这样位于该Agenda Group 当中的规则才会

触发执行,否则将不执行。示例:

rule "rule1"

agenda-group "001"

when

eval(true)

then

System.out.println("rule1 execute");

end

rule "rule 2"

agenda-group "002"

when

eval(true)

then

System.out.println("rule2 execute");

End

Java测试代码,注意foucs

public class Test {

public static void main(String[] args) {

KnowledgeBuilder kb =

KnowledgeBuilderFactory.newKnowledgeBuilder();

kb.add(new ClassPathResource("test/test.drl"),

ResourceType.DRL);

Collection collection = kb.getKnowledgePackages();

KnowledgeBase knowledgeBase =

KnowledgeBaseFactory.newKnowledgeBase();

knowledgeBase.addKnowledgePackages(collection);

StatefulKnowledgeSession statefulSession = knowledgeBase

.newStatefulKnowledgeSession();

statefulSession.getAgenda().getAgendaGroup("002").setFocus();

statefulSession.fireAllRules();

statefulSession.dispose();

}

}

statefulSession.getAgenda().getAgendaGroup("002").setFocus();

这句代码的作用是先得到当前的Agenda,再通过Agenda 得到名为002 的Agenda Group对象,最后把Focus 设置到名为002 的Agenda Group 当中,这个位于名为002 的Agenda Group中的rule2 规则会执行,而位于名为001 的Agenda Group 当中的rule1 则不会被执行,因为名为001 的Agenda Group 没有得到Focus。

实际应用当中agenda-group 可以和auto-focus 属性一起使用,这样就不会在代码当中显示的为某个Agenda Group 设置Focus 了。一旦将某个规则的auto-focus 属性设置为true,那么即使该规则设置了agenda-group 属性,我们也不需要在代码当中显示的设置Agenda Group的Focus 了。

4.5.11 auto-focus

前面我们也提到auto-focus 属性,它的作用是用来在已设置了agenda-group 的规则上设置该规则是否可以自动独取Focus,如果该属性设置为true,那么在引擎执行时,就不需要显示的为某个Agenda Group 设置Focus,否则需要。

对于规则的执行的控制,还可以使用Agenda Filter 来实现。在Drools 当中,提供了一个名为org.drools.runtime.rule.AgendaFilter 的Agenda Filter 接口,用户可以实现该接口,通过规则当中的某些属性来控制规则要不要执行。org.drools.runtime.rule.AgendaFilter 接口只有

一个方法需要实现,方法体如下:

public boolean accept(Activation activation);

在该方法当中提供了一个Activation 参数,通过该参数我们可以得到当前正在执行的规则对象或其它一些属性,该方法要返回一个布尔值,该布尔值就决定了要不要执行当前这个规则,返回true 就执行规则,否则就不执行。

在引擎执行规则的时候,我们希望使用规则名来对要执行的规则做一个过滤,此时就可以通过AgendaFilter 来实现,示例代码既为我们实现的一个AgendaFilter 类源码。

import org.drools.runtime.rule.Activation;

import org.drools.runtime.rule.AgendaFilter;

public class TestAgendaFilter implements AgendaFilter {

private String startName;

public TestAgendaFilter(String startName){

this.startName=startName;

}

public boolean accept(Activation activation) {

String ruleName=activation.getRule().getName();

if(ruleName.startsWith(this.startName)){

return true;

}else{

return false;

}

}

}

从实现类中可以看到,我们采用的过滤方法是规则名的前缀,通过Activation 得到当前的Rule 对象,然后得到当前规则的name,再用这个name 与给定的name 前缀进行比较,如果相同就返回true,否则就返回false。

4.5.12 ruleflow-group

在使用规则流的时候要用到ruleflow-group 属性,该属性的值为一个字符串,作用是用来将规则划分为一个个的组,然后在规则流当中通过使用ruleflow-group 属性的值,从而使用对应的规则。后面在讨论规则流的时候还要对该属性进行详细介绍。

4.6、注释

在编写规则的时候适当添加注释是一种好的习惯,好的注释不仅仅可以帮助别人理解你编写的规则也可以帮助你自己日后维护规则。在Drools 当中注释的写法与编写Java 类的注释的写法完全相同,注释的写法分两种:单行注释与多行注释。

单行注释可以采用“#”或者“//”来进行标记,如代码片段2-45 中的规则就添加了两个注释,一个以“//”开头,一个是以“#”开头。

如果要注释的内容较多,可以采用Drools 当中的多行注释标记来实现。Drools 当中的多行注释标记与Java 语法完全一样,以“/*”开始,以“*/”结束。

4.7、函数

函数是定义在规则文件当中一代码块,作用是将在规则文件当中若干个规则都会用到的业务操作封装起来,实现业务代码的复用,减少规则编写的工作量。函数的编写位置可以是规则文件当中package 声明后的任何地方,Drools 当中函数声明。

示例:

function void/Object functionName(Type arg...) {

/*函数体的业务代码*/

}

Drools 当中的函数以function 标记开头,如果函数体没有返回值,那么function 后面就是void,如果有返回值这里的void 要换成对应的返回值对象,接下来就是函数的名称函数名称的定义可以参考Java 类当中方法的命名原则,对于一个函数可以有若干个输入参数,

所以函数名后面的括号当中可以定义若干个输入参数。定义输入参数的方法是先声明参数类型,然后接上参数名,这点和Java 当中方法的输入参数定义是完全一样的,最后就是用“{…}”括起来的业务逻辑代码,业务代码的书写采用的是标准的Java 语法。

function void printName(String name) {

System.out.println("您的名字是:"+name);

}

该函数的名称为printName,不需要返回值,它需要一个String 类型的输入参数,函数体的业务逻辑比较简单,将输入参数组合后在控制台打印出来。

可以看到Drools 当中函数的定义与Java 当中方法的定义极为相似,与Java 当中定义方法相比,唯一不同之处就是在Drools 的函数定义当中没有可见范围的设定,而Java 当中可以通过public、private 之类来设置方法的可见范围。在Drools 当中函数的可见范围是当前的函数所在的规则文件,位于其它规则文件当中的规则是不可以调用不在本规则文件当中的函数的。所以Drools 当中函数的可见范围可以简单将其与Java 当中方法的private 类型划上等号。下面我们来看一个函数应用的例子,加深对Drools 函数的理解。

实际应用当中,可以考虑使用在Java 类当中定义静态方法的办法来替代在规则文件当中定义函数。我们知道Java 类当中的静态方法,不需要将该类实例化就可以使用该方法,利用这种特性,Drools 为我们提供了一个特殊的import 语句:import function,通过该import语句,可以实现将一个Java 类中静态方法引入到一个规则文件当中,使得该文件当中的规则可以像使用普通的Drools 函数一样来使用Java 类中某个静态方法。

import function test.RuleTools.printInfo;

通过使用import function 关键字,将test.RuleTools 类中静态方法

printInfo 引入到当前规则文件中,在名为rule1 的规则的RHS 当中,将printInfo 静态方法作为一个普通的Drools 函数来进行使用。

四、查询

查询是Drools 当中提供的一种根据条件在当前的WorkingMemory 当中查找Fact 的方法。查询是定义在规则文件当中,和函数一样,查询的定义可以是package 语句下的任意位置,在Drools 当中查询可分为两种:一种是不需要外部传入参数;一种是需要外部传入参数。下面我们就分别对这两种类型的查询进行介绍。

4.1、无参数查询

在 Drools 当中查询以query 关键字开始,以end 关键字结束,在package 当中一个查询要有唯一的名称,查询的内容就是查询的条件部分,条件部分内容的写法与规则的LHS 部分写法完全相同。

query "query name"

#conditions

End

查询的调用是由StatefulSession 完成的, 通过调用StatefulSession 对象的getQueryResults(String queryName)方法实现对查询的调用,该方法的调用会返回一个QueryResults 对象,QueryResults 是一个类似于Collection 接口的集合对象,在它当中存放在若干个QueryResultsRow 对象,通过QueryResultsRow 可以得到对应的Fact 对象,从而实现根据条件对当前WorkingMemory 当中Fact 对象的查询。

QueryResults queryResults=statefulSession.getQueryResults("testQuery");for(QueryResultsRow qr:queryResults){Customer cus=(Customer)qr.get("customer");//打印查询结果System.out.println("customer name :"+cus.getName());
}

2参数查询

和函数一样,查询也可以接收外部传入参数,对于可以接收外部参数的查询格式如下:

query "query name" (Object obj,...)

#conditions

end

和不带参数的查询相比,唯一不同之外就是在查询名称后面多了一个用括号括起来的输入参数,查询可接收多个参数,多个参数之间用“,”分隔,每个参数都要有对应的类型声明

query "testQuery"(int $age,String $gender)

customer:Customer(age>$age,gender==$gender)

end

对于带参数的查询,可以采用StatefulSession 提供的getQueryResults(String queryName,new Object[]{})方法来实现,这个方法中第一个参数为查询的名称,第二个Object对象数组既为要输入的参数集合,示例:

QueryResults queryResults=statefulSession.getQueryResults("testQuery", newObject[]{new Integer(20),"F"});for(QueryResultsRow qr:queryResults){Customer cus=(Customer)qr.get("customer");//打印查询结果System.out.println("customer name :"+cus.getName());
}

五 对象定义

在 Drools 当中,可以定义两种类型的对象:一种是普通的类型Java Fact 的对象;另一种是用来描述Fact 对象或其属性的元数据对象。

5.1、Fact对象定义

我们知道在Drools 当中是通过向WorkingMemory 中插入Fact 对象的方式来实现规则引擎与业务数据的交互,对于Fact 对象就是普通的具有若干个属性及其对应的getter 与setter方法的JavaBean 对象。Drools 除了可以接受用户在外部向WorkingMemory 当中插入现成的Fact 对象,还允许用户在规则文件当中定义一个新的Fact 对象。

在规则文件当中定义Fact 对象要以declare 关键字开头,以end 关键字结尾,中间部分就是该Fact 对象的属性名及其类型等信息的声明。代码清单2-57 是一个简单的在规则当中定义的Fact 对象示例。

代码清单

declare Address

city : String

addressName : String

end

和如下Java代码等价

public class Address {

private String city;

private String addressName;

public String getCity() {

return city;

}

public void setCity(String city) {

this.city = city;

}

public String getAddressName() {

return addressName;

}

public void setAddressName(String addressName) {

this.addressName = addressName;

}

}

在使用declare 关键字来声明一个Fact 对象时,对于属性类型的定义,除了可以使用类似java.lang.String 之类的简单类型之外,也可以使用另外一些JavaBean 作为某个属性的类型。

5.2、元数据定义

我们知道,元数据就是用来描述数据的数据。在Drools5 当中,可以为Fact 对象、Fact对象的属性或者是规则来定义元数据,元数据定义采用的是“@”符号开头,后面是元数据的属性名(属性名可以是任意的),然后是括号,括号当中是该元数据属性对应的具体值(值是任意的)。具体格式如下:

@metadata_key( metadata_value )

在这里metadata_value 属性值是可选的。具体实例如下:

@author(gaojie)

这个元数据属性名为author,值为gaojie。

declare User

@author(gaojie)

@createTime(2009-10-25)

username : String @maxLenth(30)

userid : String @key

birthday : java.util.Date

end

代码是一个元数据在通过declare 关键字声明对象时的具体应用实例,在这个例子当中,一共有四个元数据的声明,首先对于User 对象有两个元数据的声明:author 说明这个对象的创建者是gaojie;createTime 说明创建的时间为2009-10-25 日。接下来是对User对象的username 属性的描述,说明该属性的maxLenth 的值为30。最后是对userid 属性的描述,注意这个描述只有一个无数据的属性名key,而没有为这个属性key 赋于具体的值。

Drools规则引擎之常用语法相关推荐

  1. drools规则引擎的在项目中的使用手记

    需求 按照登录用户的会员等级 和签到周期 根据一定的计算规则送积分.由于之前都是通过if else去做的控制.规则变更的时候可能需要重新调整代码甚至发布服务. 由于不想再每次规则变更后需要调整代码,于 ...

  2. 详解:Drools规则引擎探究

    引入 ▐ 问题引入 天猫奢品业务方为了吸引更多的新客,和提高会员的活跃度,做了一期活动,通过购买天猫奢品频道内的任意商品就赠送特殊积分,积分可以直接兑换限量的奢品商品.假如业务方给的规则如下: 主刃同 ...

  3. 使用 Drools 规则引擎实现业务逻辑,可调试drl文件

    http://www.srcsky.com/tech/arts/389.html 代码下载http://download.csdn.net/detail/zhy011525/2462313 使用 Dr ...

  4. drools规则引擎因为内存泄露导致的内存溢出

    进入这个问题之前,先了解一下drools: 在很多行业应用中比如银行.保险领域,业务规则往往非常复杂,并且规则处于不断更新变化中,而现有很多系统做法基本上都是将业务规则绑定在程序代码中. 主要存在的问 ...

  5. SpringBoot2 整合 Drools规则引擎,实现高效的业务规则

    本文源码:GitHub·点这里 || GitEE·点这里 一.Drools引擎简介 1.基础简介 Drools是一个基于java的规则引擎,开源的,可以将复杂多变的规则从硬编码中解放出来,以规则脚本的 ...

  6. SpringBoot整合Drools规则引擎动态生成业务规则

    最近的项目中,使用的是flowable工作流来处理业务流程,但是在业务规则的配置中,是在代码中直接固定写死的,领导说这样不好,需要规则可以动态变化,可以通过页面去动态配置改变,所以就花了几天时间去研究 ...

  7. drools规则引擎 java_Drools规则引擎的使用总结

    前一段时间在开发了一个做文本分析的项目.在项目技术选型的过程中,尝试使用了Drools规则引擎.让它来作为项目中有关模式分析和关键词匹配的任务.但后来,因为某种原因,还是撇开了Drools.现将这个过 ...

  8. 使用 Drools 规则引擎实现业务逻辑

    要求施加在当今软件产品上的大多数复杂性是行为和功能方面的,从而导致组件实现具有复杂的业务逻辑.实现 J2EE 或 J2SE 应用程序中业务逻辑最常见的方法是编写 Java 代码来实现需求文档的规则和逻 ...

  9. drools规则引擎的基本使用和原理介绍

    理论基石 借用:<drools规则引擎技术指南>来说, drools是: 开源项目,规则引擎技术,规则语法形成的规则文件,可以存在数据库等,通过drools包提供的接口,调用生成对应的结果 ...

最新文章

  1. 关于CSS的长度单位及颜色表示
  2. 比較++和+的运算符优先级
  3. LINQ to SQL语句(1)之Where(抄的好)
  4. livecd制作 centos
  5. poj hdu Tian Ji -- The Horse Racing 贪心
  6. java能过吗_java – 你能通过例子解释AspectJ的cFlow(P u00...
  7. Python---时间函数
  8. 关于 .NET 与 JAVA 在 JIT 编译上的一些差异
  9. 服务器无法通过系统非页面共享区进行分配,服务器无法通过系统非页面共享区来进行分配的解决方法...
  10. 重读经典:《An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale》
  11. mysql 防重复提交_怎样防止刷新重复提交、防后退
  12. Tomcat学习总结(13)—— Tomcat常用参数配置说明
  13. python counter 出现次数最少的元素_[PY3]——找出一个序列中出现次数最多的元素/collections.Counter 类的用法...
  14. Linux下修改PATH的方法
  15. SMC 如何下载三维及二维图
  16. 将oracle卸载干净 远离废弃oracle
  17. Origin | 图形动画制作
  18. matlab实现退化模型,数字图像处理Matlab退化模型示例(example0507).docx
  19. 2014年禁毒工作总结,2015年工作打算
  20. CAD能打开PDF格式吗?这样做可以快速实现

热门文章

  1. 地图采集车的那些事 | 硬件质量篇
  2. SQL语句怎么去掉字段不想要的字符?
  3. 什么是网站站群?网站站群有哪些类型?
  4. 【从零开始学爬虫】采集B站UP主数据
  5. 一个数的亲和数iiic++_小学1-6年级数学公式和定律
  6. MobileNet论文翻译
  7. Incorrect column count: expected 1, actual 5
  8. Pylint同时屏蔽多个报错信息
  9. 行业典型案例解读 | FASS全闪高性能文件网关解决方案
  10. bp神经网络 鸢尾花matlab,神经网络(BP网)—鸢尾花分类问题.pdf