• Swift 高阶函数详解(forEach、filter、map、flatMap、compactMap、reduce、sorted)


    前言

    在学 Swift 的时候,就觉得 Swift 提供的几个高阶函数特别好用,每次有使用场景时,都会查资料看看怎么用。现在不知不觉已经写了一年多 Swift 了,这几个高阶函数也用得很熟练了,但是写文章记录这件事也因为各种工作、生活上的事情一拖再拖。

    现在这篇关于高阶函数的介绍,它来了。内容主要是介绍 forEach、filter、map、flatMap、compactMap、reduce、sorted等 7 个函数的作用,以及他们的使用方式。本文可以当做字典按需查阅。

    高阶函数介绍

    1. forEach

    forEach 函数的作用 for - in 基本一致, 它接受一个闭包参数,该闭包接受序列中的一个元素作为参数并执行某些操作。时间复杂度是 O ( n ) O(n) O(n)

    函数的形式如下:

    func forEach(_ body: (Element) throws -> Void) rethrows
    
    • 1

    使用例子如下:

    let numberWords = ["one", "two", "three"]
    
    // 遍历数组中的结果并打印
    numberWords.forEach { word in
    	print(word)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    需要注意的是:forEach 函数没有返回值,并且不能在操作函数中改变集合中的元素

    2. filter

    filter 函数正如其名,它主要用于过滤数组、集合和字典等类型对象中的元素。它接受一个闭包参数,该闭包返回的结果为 true,该元素就会保留,否则就会被移除。时间复杂度是 O ( n ) O(n) O(n)

    函数的形式如下:

    func filter(_ isIncluded: (Element) throws -> Bool) rethrows -> [Element]
    
    • 1

    下面是一个简单的例子,是利用 filter 函数过滤数组中的偶数,并返回一个新的数组:

    let nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    let filteredNums = nums.filter { $0 % 2 == 0 }
    print(filteredNums)  // 输出 [2, 4, 6, 8, 10]
    
    • 1
    • 2
    • 3

    在上面的示例中,闭包表达式 { $0 % 2 == 0 } 接受一个参数 $0,判断 $0 是否为偶数,如果是则返回 true,否则返回 false。

    3. map

    map() 函数用于对集合中的每个元素进行转换,并返回一个新的集合,其中包含转换后的元素。时间复杂度是 O ( n ) O(n) O(n)

    函数的形式如下:

    func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
    
    • 1

    下面是用 map 做数组类型转换的例子:

    struct Person {
        var name: String
        var age: Int
    }
    
    // 创建一个Person对象数组
    let peoples = [
        Person(name: "Alice", age: 30),
        Person(name: "Bob", age: 25),
        Person(name: "Charlie", age: 35)
    ]
    
    // [Person] -> [String]
    let names = peoples.map { $0.name }
    
    print(names)  // 输出:["Bob", "Alice", "Charlie"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    需要注意的是,使用 map 函数不会自动帮我们去掉转换失败的 nil 值,如有相关诉求可以使用 compactMap 函数。

    4. flatMap

    flatMap 函数和上面提到的 map 函数相似,也是用于对每个元素做转换,返回一个新的集合;但是不太一样的是,它对每个元素转换的返回值可以是集合类型的,然后会将所有的结果集合合并成一个,常用于做数据打平操作(例如将二维数组转一维)。时间复杂度是 O ( n + m ) O(n + m) O(n+m),其中 n n n 是数组长度, m m m 是结果长度。

    函数的形式如下:

    func flatMap<SegmentOfResult>(_ transform: (Element) throws -> SegmentOfResult) rethrows -> [SegmentOfResult.Element] where SegmentOfResult : Sequence
    
    • 1

    下面是利用 flatMap 函数将二维数组转一维的例子:

    let numbers = [1, 2, 3, 4]
    
    // [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]
    let mapped = numbers.map { Array(repeating: $0, count: $0) }
         
    let flatMapped = numbers.flatMap { Array(repeating: $0, count: $0) }
    print(flatMapped) // 输出: [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5. compactMap

    compactMap 函数的作用和 mapflatMap 函数一样,唯一不同的是它会剔除结果集合中转换失败的 nil。时间复杂度是 O ( n + m ) O(n + m) O(n+m),其中 n n n 是数组长度, m m m 是结果长度。

    函数的形式如下:

    func compactMap<ElementOfResult>(_ transform: (Element) throws -> ElementOfResult?) rethrows -> [ElementOfResult]
    
    • 1

    下面是利用 mapcompactMap 做对象转换的对比例子。

    let possibleNumbers = ["1", "2", "three", "///4///", "5"]
    
    let mapped: [Int?] = possibleNumbers.map { str in Int(str) }
    print(mapped) // Prints: [1, 2, nil, nil, 5]
    
    // 通过 compactMap 转换,会剔除转换失败产生的 nil 值
    let compactMapped: [Int] = possibleNumbers.compactMap { str in Int(str) }
    print(compactMapped) // Prints: [1, 2, 5]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    6. reduce

    reduce 函数主要用于遍历集合类型对象来进行求值,或构造一个新集合类型对象等,时间复杂度是 O ( n ) O(n) O(n)

    函数的形式如下:

    // 传入闭包有返回值,常用于遍历求值
    func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
    
    // 传入闭包无返回值,常用构建新集合类型对象
    func reduce<Result>(into initialResult: Result, _ updateAccumulatingResult: (inout Result, Element) throws -> ()) rethrows -> Result
    
    • 1
    • 2
    • 3
    • 4
    • 5

    6.1 对数组求和

    let sizes = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
    // 利用 reduce 求元素累加
    let totalSize = sizes.reduce(0, { $0 + $1 })
    print(totalSize) // 输出: 30
    
    • 1
    • 2
    • 3
    • 4

    如果只是单纯想做累加,或者累乘操作,可以像下面简写成运算符号。

    // 利用 reduce 求元素累加
    let totalSize = sizes.reduce(0, +)
    
    • 1
    • 2

    6.2 求数组最值

    let nums = [1, 4, 54, -4, 5]
    // 求最大值
    let minNum = nums.reduce(Int.max) { partialResult, num in
        return min(partialResult, num)
    }
    print(minNum) // 输出: -4
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    6.3 构建新集合类型对象

    下面是一个利用 reduce 函数构建字典的例子

    struct Person {
        var name: String
        var age: Int
    }
    
    // 创建一个Person数组对象
    let peoples = [
        Person(name: "Alice", age: 30),
        Person(name: "Bob", age: 25),
        Person(name: "Charlie", age: 35)
    ]
    
    // 构建一个 key 是 name,value 是 age 的数组
    let infoMap = peoples.reduce(into: [String: Int]()) { partialResult, person in
        partialResult[person.name] = person.age
    }
    
    print(infoMap) //输出: ["Charlie": 35, "Bob": 25, "Alice": 30]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    7. sorted

    sorted 是 swift 提供的排序函数,时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)

    函数的形式如下:

    // 对数组原地排序,要数组对象声明为 `var`
    mutating func sort(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows
    
    // 对数组排序,不改变原来的数组,返回结果数组
    func sorted(by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    7.1 对基本类型排序

    下面是将字符串按字典序从小到大排序例子

    let students: Set = ["Kofi", "Abena", "Peter", "Kweku", "Akosua"]
    // 排序
    let sortedStudents = students.sorted(by: {$0 < $1 })
    print(sortedStudents)
    // Prints: ["Abena", "Akosua", "Kofi", "Kweku", "Peter"]
    
    • 1
    • 2
    • 3
    • 4
    • 5

    还有下面这种简便写法,直接用比较符号表示升降序。

    let sortedStudents = students.sorted(by: <)
    
    • 1

    也可以这样,因为默认传的就是 <

    let sortedStudents = students.sorted()
    
    • 1

    需要注意的是,如果你对自定义类型使用这种简便写法,那么你需要为该类型实现 Comparable 协议,以便排序算法能够正确比较元素的大小和相等性。

    7.2 对自定义类型排序

    下面是对自定义的 Person 数组对象,按照年龄大小降序排序的例子。

    struct Person {
        var name: String
        var age: Int
    }
    
    // 创建一个Person数组对象
    let peoples = [
        Person(name: "Alice", age: 30),
        Person(name: "Bob", age: 25),
        Person(name: "Charlie", age: 35)
    ]
    
    // 通过闭包表明 Person 对象排序规则
    let sortedPeoples = peoples.sorted { p1, p2 in
    	// 按照年龄降序排序
        return p1.age > p2.age
    }
    
    print(sortedPeoples)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    按照前文提到的,如果要对自定义类型使用简便写法(传入的比较的闭包简写成符号),就要让自定义类型实现 Comparable 协议。

    // 实现 Comparable 协议
    // MARK: - Comparable
    extension Person: Comparable {
        static func < (lhs: Person, rhs: Person) -> Bool {
            return lhs.age < rhs.age
        }
    }
    
    // 使用简便写法
    let sortedPeoples = peoples.sorted(by: >)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    参考资料

    https://developer.apple.com/documentation/swift/sequence/

  • 相关阅读:
    .net lambda表达式合并
    基于SSM框架的图片分享及评价网站设计与实现 毕业设计-附源码201524
    【问题思考总结】武忠祥排除法漏洞(考研数学)
    地方(少数民族)节假日
    IP 电话
    Docker Minio
    JavaCV音视频开发宝典:rtsp转推到rtp(非TS流方式),及使用TS流发送解决sdp缺失问题
    y97.第六章 微服务、服务网格及Envoy实战 -- xDS API与动态配置(八)
    python使用dataset快速使用SQLite
    python selenium下载一个合适的chromedriver.exe(稳定版本)
  • 原文地址:https://blog.csdn.net/weixin_42292229/article/details/131971680