在 Drools 的集合中找到最好的 3 个元素

Finding the best 3 elements in a collection in Drools

假设我的工作记忆中有一些数字,并且在规则中我需要检查 3 个更高的数字是否有一个 属性(例如它们都是奇数),你会怎么写在纯 Drools 中?

我可以收集列表中的所有数字,然后有一个 Java 函数过滤它,只取最好的 3 个(比如排序列表并取最后 3 个),然后检查 属性 使用 Drools 的那 3 个,但我想知道这是否可以在纯 Drools 中完成,也许可以通过积累来完成?

想了想也没找到解决办法

rule best3
when
  $o1: Integer( $i1: intValue, intValue % 2 == 0 )
  not Integer( intValue > $i1 )
  $o2: Integer( this != $o1, $i2: intValue, intValue % 2 == 1 )
  not Integer( intValue > $i2 && < $i1 )
  Integer( this != $o1 && != $o2, $i3: intValue, intValue % 2 == 1 )
  not Integer( intValue > $i3 && < $i2 )
then
  System.out.println( $i1 + " > " + $i2 + " > " + $i3 );
end

正如我在问题中的暗示,这可以通过自定义累积函数更优雅地实现:

rule "odd"
    when
        accumulate($l : Integer(), $r : reverseSort($l); $r.size > 2, ($r[0] % 2) == 1, ($r[1] % 2) == 1, ($r[2] % 2) == 1)
    then
        System.out.println("Best 3 are odd numbers: " + $r);
end

和累加函数:

package com.test;

import java.io.*;
import java.util.*;

import org.kie.api.runtime.rule.AccumulateFunction;

public class ReverseSortAccumulator implements AccumulateFunction {
    private static class accumulatorData implements Serializable {   
        private static final long serialVersionUID = 1L;

        private List<Object> elements = new ArrayList<>();

        private List<Object> getElements() {
            return elements;
        }
        public void init() {
            getElements().clear();
        }

        public void add(Object element) {
            getElements().add(element);
        }

        public void remove(Object element) {
            getElements().remove(element);
        }

        public List<Object> reverseSort() {
            return getElements().stream()
                    .sorted(Collections.reverseOrder())
                    .collect(Collectors.toList());
        }
    }

    @Override
    public Serializable createContext() {
        return new accumulatorData();
    }

    @Override
    public void init(Serializable context) {
        getContextData(context).init();
    }

    @Override
    public void accumulate(Serializable context, Object input) {
        getContextData(context).add(input);
    }

    @Override
    public void reverse(Serializable context, Object input) {
        getContextData(context).remove(input);
    }

    @Override
    public List<?> getResult(Serializable context) {
        return getContextData(context).reverseSort();
    }

    @Override
    public boolean supportsReverse() {
        return true;
    }

    @Override
    public Class<?> getResultType() {
        return List.class;
    }

    private accumulatorData getContextData(Serializable context) {
        return (accumulatorData) context;
    }

    @Override
    public void writeExternal(ObjectOutput out) {
        // no need to externalise data over sessions
    }

    @Override
    public void readExternal(ObjectInput in) {
        // no need for externalised data from sessions
    }

}