目次
前回のブログ投稿からの続き:ルール エンジン – 「1 & (2 | 3)」 の形でのルール ロジックの抽象化、And、Or 演算子の設計の分析に焦点を当てる
Java 関数型プログラミングの基本
バイファンクション
@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
}
二項演算子
@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
}
ストリームリデュース
常见释义
英[rɪˈdjuːs] 美[rɪˈduːs]
v. 使还原; 减少; 缩小(尺寸、数量、价格等); (使)蒸发; 减轻体重; 节食;
[例句]Better insulation of your home will help to reduce heating bills.
增加房子的隔热性能会有助于减少供暖费用。
[其他] 第三人称单数:reduces 现在分词:reducing 过去式:reduced 过去分词:reduced
過負荷を軽減する 3 つの方法
// 一个参数: 主要作用 累加、累减,求取最大值、最小值。
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);
}
一部の操作で時間のかかるギャップは次のとおりです。
そして、Or 演算子
- 抽象的およびまたは
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();
}
};
}
}
- 抽象的ショートカット可能評価可能
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));
}
}
と ロジックの結合器は次のとおりです。
- とオペレーター
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 のショートカットは false、初期値は true、その後結合ロジック
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 が 1 つある限り、それは false であり、不明な条件と異常な条件の実行は個別に処理され、それ以外の場合は true を返します。
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 のショートカットが true、初期値が false の場合、結合ロジック
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 が 1 つある限り、それは true であり、不明な状態と異常な状態は個別に処理され、それ以外の場合は false が返されます。
そして、または実行
上記で説明しました、または 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 は必要ありません、単に直接真です
それ以外の場合は、コンバイナーのロジックを実行する必要があります。
したがって、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)