Комментарии 12
Последовательность начинается числом, удовлетворяющим предикату
fizz
(6, 7, 8, 9, 10)
Тут и 6 и 9 удовлетворяют предикату fizz, при этом 6 не входит, а 9 входит. Почему так?
Последовательность началась и теперь мы проверяем только "закрывающий" предикат.
В качестве альтернативы, вместо чисел можно использовать строки. Пример:
@Test
@DisplayName("Filter out lines between [```java] and [```].")
void extract_all_java_code_snippets_from_markdown_document() {
var markdown = """
# Hello, World!
The following code snippet is written in Java:
```java
System.out.println("Hello, World!");
```
The following code snippet is written in Kotlin:
```kotlin
println("Hello, World!")
```
""";
Predicate<String> fizz = "```java"::equals;
Predicate<String> buzz = "```"::equals;
// TODO: Define the predicate
Predicate<String> fizzBuzz = i -> false;
assertThat(markdown.lines().filter(fizzBuzz))
.as("Java code snippets")
.containsExactly("""
System.out.println("Hello, World!");
""");
}
.containsExactly("""
System.out.println("Hello, World!");""");
В этом кейсе работает такой вариант:
var started = new AtomicBoolean();
Predicate<String> fizzBuzz = i -> {
if (started.get()) {
if (buzz.test(i)) {
started.set(false);
return false;
} else {
return true;
}
} else {
started.set(fizz.test(i));
return false;
}
};
Как сделать это без внешнего хранения флага нахождения внутри последовательности у меня пока нет идей.
В случае с последовательностью цифр можно как-то учитывать знание о том что было раньше и что будет потом, но в случае сырого текста у нас такого знания просто нет.
var fizzBuzz = new Predicate<String> {
boolean state;
@Override
public boolean test() {
...
}
};
var fizzBuzz = new IntPredicate {
boolean state;
@Override
public boolean test() {
...
}
};
Для чисел аналогичный подход
Если это правильный ответ, то я разочарован.
Можете предложить своё решение. С интересом посмотрим.
Дело не в решении, а в задаче скорее. Смотрите, вы всю статью показываете как, комбинируя предикаты, добиться результатов из примеров. В последней задаче ожидаешь какого-то аналогичного решения. Т.е. комбинации логических операторов. Начинаешь думать, понимая, что так не получится. Периодически отбрасываешь вариант с состоянием, как нечто чужеродное в данном контексте. А потом выясняешь, что так и надо было. Вот от этого разочарование.
Ну и в остальном, предикаты с состоянием это так себе идея в реальной жизни. Даже без учета того, что кто-то может засунуть их в parallelStream
Если вы про решение последней задачки, то решение довольно простое. Нужно исключить начало интервала и конец, значит остаток от деления на 3 и на 5 должен быть больше 0. Также нужно исключить общие множители, значит остатки от деления не должны быть равны. И нужно исключить наложение отрезков, т.е. когда остаток от деления равен 4 (границы не включаются). Если оформлять решение через демонстрированный в статье метод предикатов, то получаем:
решение
var numbers = IntStream.rangeClosed(1, 30);
IntPredicate startFizz = i -> i % 3 > 0;
IntPredicate startFizzException = i -> i % 5 == 4;
IntPredicate endBuzz = i -> (i % 3) < (i % 5);
assertThat(numbers.filter(startFizz.or(startFizzException).and(endBuzz)))
.as("Numbers between integers divisible by three and by five")
.containsExactly(4, 7, 8, 9, 13, 14, 19, 22, 23, 24, 28, 29);
Действительно, состояние можно затащить внутрь и сэкономить на AtomicBoolean
.
Последовательность начинается числом, удовлетворяющим предикату
fizz
, и заканчивается числом, удовлетворяющим предикатуbuzz
.
Последовательность начатая 6
-кой во время 9
-ки ещё не завершилась - она завершится только на 10
-ке и 9
-ка окажется внутри последовательности [6, 10]
.
FizzBuzz и искусство фильтрации: Stream API и предикаты