reduce(_:_ :)函数迅速起作用了吗?

时间:2019-05-08 06:17:10

标签: swift

这是我不明白的一段代码。这段代码使用swift的reduce( :)函数以及我难以理解的闭包。 maxVerticalPipCount和maxHorizo​​ntalPipCount中设置的值是什么?它们分别是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 个答案:

答案 0 :(得分:1)

reduce函数遍历集合中的每个项目,并将它们组合为一个值。可以认为它实际上是将多个值减少为一个值。 [Source]

来自Apple Docs

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

在您的代码中

maxVerticalPipCount遍历整个数组,并在每次迭代的第二个元素和第一个元素的计数之间找到最大值。

maxHorizontalPipCount正在找到第二个元素的最大值和第一个元素的最大值。

请尝试在reduce函数中打印每个元素,以便更好地理解。

let maxVerticalPipCount = pipsPerRowForRank.reduce(0) {
    print($0)
    return max($1.count, $0)
}

答案 1 :(得分:1)

Reduce将数组中的所有数字加在一起会打开一个闭包,并真正执行您要返回的所有操作。

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

此处从0的{​​{1}}开始,并遍历整个数组。它在计算中的上一个值与子数组中的项目数之间取最大值。在上面的示例中,该过程将是:

reduce(0)

关于第二个

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

在这里,我们不是检查数组的计数,而是检查嵌套数组的最大值,除非它为空,否则它为0。所以这里是这个:

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

答案 2 :(得分:1)

这就是reduce函数在这里所做的

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)
    }
}

您不应使用reduce(::)函数来查找最大值。像这样使用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)

答案 3 :(得分:1)

顺便说一句,如果您想知道reduce的确切功能,可以随时参考source code,在其中您可以看到实际的代码以及注释中的漂亮叙述性描述。

但是您问题的根源在于这段代码并不完全清楚。我可能会建议,如果您难以理解代码片段,可以用有意义的名称替换不透明的速记参数名称$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

在我看来,这使意图非常明确,即我们正在计算每个子数组中有多少项并返回最大计数。

同样,对于水平方向:

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

同样,我认为很明显,我们正在创建值的平面数组,然后获得最大值。

而且,在两种情况下,我们都使用lazy来避免构建临时结构(如果我们的数组很大),但是我们会对其进行评估。这改善了例程的存储特性,并且生成的代码更加有效。坦白地说,对于如此小的数组,lazy并不是必需的,但我将其包括在内供您参考。


最重要的是,功能性模式的目标不是编写尽可能少的击键代码(因为我们可以编写出更多简洁的摘要),而是编写高效的代码,其意图应尽可能地清晰数量但是我们应该总是能够快速浏览一下代码并对其进行推理。有时,如果需要进一步优化,出于性能原因,我们会做出有意识的决定,以牺牲可读性为代价,但这不是必需的。

答案 4 :(得分:0)

使用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)
}