Java-предикаты с распределительным законом

#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.x

2. Если вы просто пишете лямбду без явного ввода, 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");
    }
}