Как пребразовать IntStream в for. Как применяется IntUnaryOperator
Код из codesignal.com Помогите разобраться, как он работает. В частности, можно ли IntStream переделать как for для понимания.
IntUnaryOperator - почему выбран и что он делает. Представление о функциональных интерфейсах имею, но на уровне примитивных примеров.
boolean solution(char[][] grid) {
return IntStream.range(0, 9)
.allMatch(n ->
isValid(grid, k -> k, k -> n) &&
isValid(grid, k -> n, k -> k) &&
isValid(grid, k -> (n / 3) * 3 + k / 3, k -> (n % 3) * 3 + k % 3));
}
boolean isValid(char[][] grid, IntUnaryOperator rowSelector, IntUnaryOperator colSelector) {
Set<Character> s = new HashSet<>();
for (int k = 0; k < 9; k++) {
int r = rowSelector.applyAsInt(k);
int c = colSelector.applyAsInt(k);
if (grid[r][c] == '.') {
continue;
}
if (s.contains(grid[r][c])) {
return false;
}
s.add(grid[r][c]);
}
return true;
}
Ответы (2 шт):
Сам по себе IntStream.range(0, 9) - это примерно как for (int i = 0; i < 9; i++).
allMatch - это проверка, что все элементы из stream соответствуют указанному условию (чтобы allMatch вернул true, нужно чтобы для каждого элемента из stream предикат вернул true). Если переделывать в обычный цикл, то внутри цикла должен быть if, который выйдет из цикла, если предикат вернул false.
Замена на for и if будет примерно такая:
boolean solution(char[][] grid) {
for (int i = 0; i < 9; i++) {
IntPredicate predicate = n ->
isValid(grid, k -> k, k -> n) &&
isValid(grid, k -> n, k -> k) &&
isValid(grid, k -> (n / 3) * 3 + k / 3, k -> (n % 3) * 3 + k % 3);
if (!predicate.test(i)) {
return false;
}
}
return true;
}
IntUnaryOperator - просто функциональный интерфейс, который можно реализовать функцией, принимающей целое число и возвращающим целое число (т.е. операция над целым числом).
Примерно то же самое, что Function<Integer, Integer>. В коде метода isValid можно заменить IntUnaryOperator на Function<Integer, Integer> и applyAsInt на просто apply, и все будет работать как работало.
Заменить IntStream на цикл for в методе solution достаточно просто, но читабельность кода вряд ли улучшится от этого:
boolean solution(char[][] grid) {
for (int n = 0; n < 9; n++) {
if (!(isValid(grid, k -> k, k -> n) &&
isValid(grid, k -> n, k -> k) &&
isValid(grid, k -> (n / 3) * 3 + k / 3, k -> (n % 3) * 3 + k % 3)
)) {
return false;
}
}
return true;
}
IntUnaryOperator в данном случае представляют собой некоторые простые функции для выбора ряда или строки для заданного числа.
isValid(grid, k -> k, k -> n)
ЗдесьrowSelectorвозвращает свой аргумент, аcolSelector-- константное значение в диапазоне[0 .. 9).
Таким образом внутри методаisValidпри подставлении числаkв том же диапазоне[0 .. 9)будет выполняться итерация по строкам:r ∈ [0..9), c = nдля заданногоn-го столбца, т.е. выполняется вертикальная проверка.isValid(grid, k -> n, k -> k)
Аналогично,rowSelectorвозвращает константное значение в диапазоне[0 .. 9), аcolSelector-- свой аргумент, то есть внутри методаisValidбудет выполняться итерация по столбцам для заданнойn-ой строки:r = n, с ∈ [0..9)-- выполняется горизонтальная проверкаisValid(grid, k -> (n / 3) * 3 + k / 3, k -> (n % 3) * 3 + k % 3)- для проверки квадратов 3х3 внутри входного массива символов 9х9.
То есть, изменяя вычисление индексов, достаточно использовать одну и ту же функцию для проверки строки / столбца / квадрата.
Решение в старом "нефункциональном" стиле могло бы выглядеть так (с использованием вложенного цикла):
static boolean solution(char[][] grid) {
for (int n = 0; n < 9; n++) {
Set<Character> row = new HashSet<>();
Set<Character> col = new HashSet<>();
Set<Character> sqr = new HashSet<>();
for (int k = 0; k < 9; k++) {
// рассматриваем строку
char r = grid[n][k];
if (r != '.' && !row.add(r)) return false;
// рассматриваем столбец
char c = grid[k][n];
if (c != '.' && !col.add(c)) return false;
// рассматриваем квадрат 3х3
char s = grid[(n / 3) * 3 + k / 3][ (n % 3) * 3 + k % 3];
if (s != '.' && !sqr.add(s)) return false;
}
}
return true;
}
Очевидно, здесь часть кода дублируется, отличаясь только способом вычисления индексов и добавлением элементов в соответствующий сет для проверки уникальности.