在Swift中,reduce(::)函数做什么?

在Swift中,reduce(::)函数做什么?,swift,Swift,这是一段我不懂的代码。这段代码使用swift的reduce(::)函数和闭包,我很难理解它。在maxVerticalPipCount和maxHorizontalPipCount中设置了哪些值?它们分别是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],

这是一段我不懂的代码。这段代码使用swift的reduce(::)函数和闭包,我很难理解它。在
maxVerticalPipCount
maxHorizontalPipCount
中设置了哪些值?它们分别是
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) })

reduce函数在集合中的每个项上循环,并将它们组合成一个值。可以将其视为将多个值减少为一个值。[]


在代码中

maxVerticalPipCount
正在迭代整个数组,并查找每次迭代的第二个元素和第一个元素之间的最大计数

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

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

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

Reduce将数组中的所有数字相加,打开一个闭包,并真正执行您让它返回的任何操作

let pipsPerRowForRank = [[1,1], [2,2,2]]
let maxVerticalPipCount = CGFloat(pipsPerRowForRank.reduce(0) { 
                              max($1.count, $0)})
在这里,它从
0
at
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

这就是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)
    }
}
您不应该使用函数来查找最大值。使用这样的函数

let maxVerticalPipCount = CGFloat(pipsPerRowForRank.max { $0.count < $1.count }?.count ?? 0)
let maxHorizontalPipCount = CGFloat(pipsPerRowForRank.max { ($0.max() ?? 0) < ($1.max() ?? 0) }?.max() ?? 0)
让maxVerticalPipCount=CGFloat(pipsPerRowForRank.max{$0.count<$1.count}?.count??0)
设maxHorizontalPipCount=CGFloat(pipsPerRowForRank.max{($0.max()??0)<($1.max()??0)}.max()??0)

顺便说一句,如果您想知道
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)
}
通过使用使函数意图更清晰的参数名,通常更容易了解代码正在做什么。IMHO,特别是当有多个参数时,使用显式参数名称可以使其更加清晰


话虽如此,我可能不会使用
reduce
,而是执行以下操作:

let verticalMax = pipsPerRowForRank
    .lazy
    .map { $0.count }
    .max() ?? 0
在我看来,这使得意图非常清楚,即我们正在计算每个子数组中有多少个项目,并返回最大数量

同样,对于水平面:

let horizontalMax = pipsPerRowForRank
    .lazy
    .flatMap { $0 }
    .max() ?? 0
同样,我认为这很清楚,我们正在创建一个值的平面数组,然后获得最大值

而且,在这两种情况下,我们都使用它来避免构建临时结构(如果我们的阵列非常大的话),但在进行过程中对其进行评估。这改善了例程的内存特性,生成的代码效率更高。坦率地说,对于这么小的数组,
lazy
是不需要的,但我将其包括在内供您参考


归根结底,函数模式的目标不是用尽可能少的击键来编写代码(因为我们可以编写更简洁的格式副本),而是用最少的粗制滥造来编写目的尽可能清晰的高效代码。但是我们应该总是能够快速浏览代码并对其进行推理。有时,如果需要进一步优化,我们会有意识地决定为了性能原因牺牲可读性,但这里不需要这样做。

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)
}
enum错误:错误{
案例错误
}
设数字=[1,2,3,4,5]
设inititalValue=0
让sum=numbers.reduce(Result.success(inititalValue)){(Result,value)->Result in
如果让initialValue=try?result.get(){
return.success(value+initialValue)
}否则{
return.failure(Errors.someError)
}
}
开关和{
成功案例(让totalSum):
打印(总计)
案例。失败(let错误):
打印(错误)
}

您不明白
reduce
通常是如何工作的吗?或者只是不明白它在这种特定情况下是如何工作的?如果运行代码,会得到什么结果?我不知道你为什么要发帖问,当你可以轻松运行代码并亲自查看时,代码的结果是什么。请求帮助理解代码是可以的,但不是最终结果是什么。它们分别是5和2吗?是的,我不明白在这种特殊情况下这是怎么回事@斯威佩里想到了这样的事情。谢谢你的确认。我没有写这个代码。这是教这门课的老师写的。我理解(部分理解)它做了什么,但遇到了麻烦,因为我觉得代码做什么可以用不同的方式来完成。根据您提到的建议,我在编写代码时会牢记在心。知道如何读写这些
reduce
语句是一项很好的技能,因此从教育学的角度来看,这里有一些实用性。但是这并不是
reduce
的最佳用例,IMHO。
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)
}