Как работает функция сокращения (_: _ :) в swift? - PullRequest
1 голос
/ 08 мая 2019

Вот кусок кода, который я не понимаю. Этот код использует функцию уменьшения (: :) swift вместе с замыканием, которое мне трудно понять. Какие значения установлены в maxVerticalPipCount и maxHor horizontalPipCount? Они 5 и 2 соответственно?

    let pipsPerRowForRank = [[0], [1], [1,1], [1,1,1], [2,2], [2,1,2], 
                            [2,2,2], [2,1,2,2], [2,2,2,2], [2,2,1,2,2], 
                            [2,2,2,2,2]]
    let maxVerticalPipCount = CGFloat(pipsPerRowForRank.reduce(0) { 
                              max($1.count, $0)})
    let maxHorizontalPipCount = CGFloat(pipsPerRowForRank.reduce(0) { 
                              max($1.max() ?? 0, $0)})

Ответы [ 5 ]

2 голосов
/ 08 мая 2019

Кстати, если вам интересно, что именно reduce делает, вы всегда можете обратиться к исходному коду , где вы можете увидеть реальный код, а также хорошее повествовательное описание вкомментарии.

Но корень вашего вопроса в том, что этот код не совсем очевиден.Я могу предположить, что если вам трудно рассуждать о фрагменте кода, вы можете заменить непрозрачные сокращенные имена аргументов $0 и $1 на значимые имена, например:

let verticalMax = pipsPerRowForRank.reduce(0) { previousMax, nextArray in 
    max(nextArray.count, previousMax)
}

let horizontalMax = pipsPerRowForRank.reduce(0) { previousMax, nextArray in
    max(nextArray.max() ?? 0, previousMax)
}

Используя имена аргументов, которые делают функциональное намерение более ясным, часто проще понять, что делает код.ИМХО, особенно при наличии нескольких аргументов, использование явных имен аргументов может сделать это более ясным.


При этом я бы, вероятно, не использовал reduce и вместо этого делал бы что-то вроде:

let verticalMax = pipsPerRowForRank
    .lazy
    .map { $0.count }
    .max() ?? 0

На мой взгляд, это делает предельно ясным намерение, а именно, что мы подсчитываем, сколько элементов находится в каждом подмассиве, и возвращаем максимальное количество.

Аналогично для горизонтальногоone:

let horizontalMax = pipsPerRowForRank
    .lazy
    .flatMap { $0 }
    .max() ?? 0

Опять же, я думаю, что ясно, что мы создаем плоский массив значений, а затем получаем максимальное значение.

И в обоих случаях мыМы используем lazy, чтобы избежать создания временных структур (в случае, если наши массивы были очень большими), но оцениваем их по мере продвижения.Это улучшает характеристики памяти подпрограммы, и полученный код становится более эффективным.Честно говоря, с таким небольшим массивом массивов lazy не нужен, но я включу его для справки.


В итоге, цель с функциональными шаблонами - не писать код сНаименьшее количество возможных нажатий клавиш (поскольку мы могли бы написать более сжатые исполнения), но лучше написать эффективный код, цель которого максимально ясна с наименьшим количеством ошибок.Но мы всегда должны иметь возможность взглянуть на код и быстро обдумать его.Иногда, если необходима дальнейшая оптимизация, мы принимаем сознательное решение пожертвовать удобочитаемостью из соображений производительности, но здесь это не нужно.

1 голос
/ 08 мая 2019

Reduce складывает все числа в массиве, открывает замыкание и действительно делает все, что вы говорите, чтобы вернуть.

let pipsPerRowForRank = [[1,1], [2,2,2]]
let maxVerticalPipCount = CGFloat(pipsPerRowForRank.reduce(0) { 
                              max($1.count, $0)})

Здесь он начинается с 0 в reduce(0) и проходит по полноймассив.где он принимает наибольшее значение между его предыдущим значением, которое он находится в процессе вычисления, и количеством элементов в подмассиве.В приведенном выше примере процесс будет:

maxVerticalPipCount = max(2, 0)
maxVerticalPipCount = max(3, 2)
maxVerticalPipCount = 3

Что касается второго

let pipsPerRowForRank = [[1,2], [1,2,3], [1,2,3,4], []]
let maxHorizontalPipCount = CGFloat(pipsPerRowForRank.reduce(0) { 
                              max($1.max() ?? 0, $0)})

Здесь мы вместо проверки количества массивов проверяем максимальное значение вложенного массива,если оно пустое, то это 0. Итак, вот это:

let maxHorizontalPipCount = max(2, 0)
let maxHorizontalPipCount = max(3, 2)
let maxHorizontalPipCount = max(4, 3)
let maxHorizontalPipCount = max(0, 4)
let maxHorizontalPipCount = 4
1 голос
/ 08 мая 2019

Это то, что здесь делают функции сокращения

var maxVerticalPipCount:CGFloat = 0
for rark in pipsPerRowForRank {
    if CGFloat(rark.count) > maxVerticalPipCount {
        maxVerticalPipCount = CGFloat(rark.count)
    }
}
var maxHorizontalPipCount:CGFloat = 0
for rark in pipsPerRowForRank {
    if CGFloat(rark.max() ?? 0) > maxHorizontalPipCount {
        maxHorizontalPipCount = CGFloat(rark.max() ?? 0)
    }
}

Вы не должны использовать уменьшение (: :) для поиска максимального значения. Используйте max (by :) функция, подобная этой

let maxVerticalPipCount = CGFloat(pipsPerRowForRank.max { $0.count < $1.count }?.count ?? 0)
let maxHorizontalPipCount = CGFloat(pipsPerRowForRank.max { ($0.max() ?? 0) < ($1.max() ?? 0) }?.max() ?? 0)
1 голос
/ 08 мая 2019

Функция Reduc перебирает каждый элемент в коллекции и объединяет их в одно значение. Думайте об этом как о буквальном сведении нескольких значений к одному значению. [ Источник ]

С Apple Docs

let numbers = [1, 2, 3, 4]
let numberSum = numbers.reduce(0, { x, y in
    x + y
})
// numberSum == 10

В вашем коде

maxVerticalPipCount выполняет итерацию по всему массиву и находит максимальное значение между числом 2-го элемента и 1-го элемента каждой итерации.

maxHorizontalPipCount находит максимум максимального значения 2-го элемента и первого элемента.

Попробуйте напечатать каждый элемент внутри функции сокращения для лучшего понимания.

let maxVerticalPipCount = pipsPerRowForRank.reduce(0) {
    print($0)
    return max($1.count, $0)
}
0 голосов
/ 11 июля 2019

Пример со Swift 5,

enum Errors: Error {
    case someError
}

let numbers = [1,2,3,4,5]
let inititalValue = 0

let sum = numbers.reduce(Result.success(inititalValue)) { (result, value) -> Result<Int, Error> in
    if let initialValue = try? result.get() {
     return .success(value + initialValue)
    } else {
        return .failure(Errors.someError)
    }
}

switch sum {
case .success(let totalSum):
    print(totalSum)
case .failure(let error):
    print(error)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...