#java #predicate
Вопрос:
У меня есть логический оператор Java, который структурирован следующим образом:
возврат A И (B, C ИЛИ D)
На Яве:
return A amp;amp; (B || C || D);
Я хотел переформулировать это, используя цепные предикаты, но, похоже, мне придется использовать распределительный закон булевой алгебры, чтобы заставить его работать:
(A И B) ИЛИ (A И C) ИЛИ (A И D), что заставляет меня все время повторять A.
Это то, что я получил в java:
return A.and(B)
.or(A.and(C))
.or(A.and(D))
.test(params)
Это, похоже, не приводит к тем же результатам, так как мои тестовые примеры терпят неудачу, когда я использую версию предиката.
Возможно ли это с предикатами Java без использования закона распределения или я делаю что-то не так?
Комментарии:
1. Почему нет
A.and(B.or(C).or(D)).test(params)
?
Ответ №1:
Вы можете написать свое заявление точно так, как вы хотите:
A.and(B.or(C).or(D))
Редактировать:
Вот пример для вашего вопроса из комментариев:
// 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"));
Это немного отличается от Java до появления лямбды. Подпись Predicate.and
говорит о том, что он принимает a Predicate
. Но на самом деле это означает, что он принимает любую лямбду, которая соответствует T -> boolean
.
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);
Вы также можете использовать встроенные лямбды:
Predicate<String> p3 = (s) -> true;
p3.and(((Predicate<String>) x -> true).and(x -> true));
Резюмирую: Если вы просто пишете s -> s.equals("bar")
Java, вы не знаете, является ли эта лямбда Predicate
или любой другой функциональный интерфейс, имеющий ту же подпись T -> boolean
.
Комментарии:
1. Это сработало, но только за счет того, что B стал реальным
Predicate
. Когда я набираю B как прямой лямбда — предикат, заключенный в парантезис, это не работает, — говорит компиляторlambda expression not expected here
. С другой стороны, C и D не обязательно должны бытьPredicate
переменными, они могут быть вставлены непосредственно как(params) -> doStuff
. Похоже, я не понимаю здесь компилятор, не могли бы вы объяснить, пожалуйста? Я посмотрел подписиand
иor
в документах, но они кажутся мне равными x.x2. Если вы просто пишете лямбду без явного ввода, Java не знает, какой тип использовать.
and
иor
являются методами по умолчанию вPredicate
интерфейсе. Хотя вы должны сообщить Java, что ваша лямбда имеет типPredicate
, чтобы использовать эти методы.3. Разве Java не должна знать, какой тип использовать, когда A уже
Predicate
есть ?4. Я добавлю некоторые пояснения к своему ответу
Ответ №2:
Поскольку вы оцениваете предикат сразу после его построения, вы можете оценить все термины:
return A.test(params)
amp;amp; (B.test(params) || C.test(params) || D.test(params));
что позволяет избежать создания объектов, которые вы отбрасываете почти сразу.
Комментарии:
1. JVM сильно оптимизирована для сбора мусора объектами с коротким сроком службы. Всегда лучше писать читаемый код, а не преждевременную оптимизацию.
2. @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 amp;amp; (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");
}
}