具有分配律的 Java 谓词
Posted
技术标签:
【中文标题】具有分配律的 Java 谓词【英文标题】:Java Predicates with distributive law 【发布时间】:2021-10-20 09:15:12 【问题描述】:我有一个结构如下的布尔 Java 语句:
返回 A AND (B OR C OR D)
在 Java 中:
return A && (B || C || D);
我想通过使用链式谓词来重新表述这一点,但似乎我必须使用布尔代数的分配定律才能使其工作:
(A AND B) OR (A AND C) OR (A AND D) 这迫使我一直重复 A。
这就是我在 java 中得到的结果:
return A.and(B)
.or(A.and(C))
.or(A.and(D))
.test(params)
这似乎不会导致相同的结果,因为当我使用谓词版本时,我的测试用例会失败。
在不使用分配律的情况下,Java Predicates 是不可能的,还是我做错了什么?
【问题讨论】:
为什么不A.and(B.or(C).or(D)).test(params)
?
【参考方案1】:
您可以完全按照自己的意愿编写语句:
A.and(B.or(C).or(D))
编辑:
这里有一个来自 cmets 的问题示例:
// here you tell Java that p1 is of type Predicate
// though you can use "and" and "or"
Predicate<String> p1 = s -> s.equals("foo");
p1.and(s -> !s.equals("bar"));
但您也可以创建自己的功能接口,使用相同的签名但不同的默认方法:
public interface MyCustomPredicate<T>
boolean test(T value);
default MyCustomPredicate<T> and(MyCustomPredicate<? super T> other)
return (t) -> true;
// ...
// here you tell Java that p2 is of type MyCustomPredicate
// though you can't use "or"
// and MyCustomPredicate.and does not behave the same as Predicate.and
MyCustomPredicate p2 = s -> s.equals("bar");
p2.and(s -> !s.equals("bar"));
它与前 lambda Java 有点不同。 Predicate.and
的签名表示它接受 Predicate
。但实际上这意味着它接受与T -> boolean
匹配的任何 lambda。
Predicate<String> p1 = s -> s.equals("foo");
MyCustomPredicate p2 = s -> s.equals("bar");
// this works because MyCustomPredicate has the same T -> boolean signature as Predicate
p1.or(p2);
您也可以内联转换 lambda:
Predicate<String> p3 = (s) -> true;
p3.and(((Predicate<String>) x -> true).and(x -> true));
总结:如果你只写s -> s.equals("bar")
,Java 不知道这个lambda 是Predicate
还是任何其他具有相同签名T -> boolean
的函数式接口。
【讨论】:
这行得通,但只能使 B 成为实际的Predicate
。当我键入 B 作为由括号括起来的直接 lambda 谓词时,它不起作用 - 编译器说 lambda expression not expected here
。另一方面,C和D不一定是Predicate
变量,可以直接插入(params) -> doStuff
。好像我这里的编译器不明白,你能解释一下吗?我在文档中查看了 and
和 or
的签名,但它们似乎与我相同 x.x
如果你只写一个没有显式类型的 lambda,Java 不知道使用什么类型。 and
和 or
是 Predicate
接口中的默认方法。尽管您必须告诉 Java 您的 lambda 类型为 Predicate
才能使用这些方法。
当 A 已经是 Predicate
时,Java 是否应该知道使用什么类型?
我会在我的回答中添加一些解释【参考方案2】:
由于您在构建谓词后立即对其进行评估,因此您可以评估所有术语:
return A.test(params)
&& (B.test(params) || C.test(params) || D.test(params));
这避免了创建您几乎立即丢弃的对象。
【讨论】:
JVM 已针对生命周期较短的垃圾收集对象进行了高度优化。编写可读的代码总是比过早的优化要好。 @BenjaminM 只是指出了另一种选择。【参考方案3】:你需要使用括号
A.and(B.or(C).or(D));
这是一个验证。
Function<Boolean[], Boolean> pred = ar ->
Predicate<Boolean[]> A = a -> a[0];
Predicate<Boolean[]> B = b -> b[1];
Predicate<Boolean[]> C = c -> c[2];
Predicate<Boolean[]> D = d -> d[3];
return A.and(B.or(C).or(D)).test(ar);
;
Function<Boolean[], Boolean> java = arr ->
boolean A = arr[0];
boolean B = arr[1];
boolean C = arr[2];
boolean D = arr[3];
return A && (B || C || D);
;
Random r = new Random();
for (int i = 0; i < 1_000_000; i++)
Boolean[] b = Stream.generate(() -> r.nextBoolean())
.limit(4).toArray(Boolean[]::new);
if (java.apply(b) != pred.apply(b))
System.out.println("Different results");
【讨论】:
以上是关于具有分配律的 Java 谓词的主要内容,如果未能解决你的问题,请参考以下文章