阅读 75

Swift高阶函数案例

前言

学习Rxswift,对这些Rx操作符或者说swift高阶函数一点都不陌生,它一定程度上封装了函数转换的内部过程,解放了本应该复杂的内部逻辑,并用统一的函数名表示。其特殊性和易用性也成为了开发日常使用的香饽饽。以下关于高阶函数的内容,希望我的理解方向是对的,并和大家一起探讨。

在我们做函数表达式的时候,有太多选择去操作一个函数,在swift特性中对操作函数的过程进行省略和简化,我们将它称之为高阶函数。

随着语法朝着Swift5深入演变,高阶函数也成为了日常必需硬技能。在使用高阶函数,对函数中元素产生不同的映射结果,通过最后的组合、排序、过滤、分离,获得新的元素结果集合。如函数map、compcatMap、reduce..,为了便于理解,我将使用场景和函数类型一一区分开,也能真实的代入我们实际的开发过程中。

场景一 排序

可变数组升降序排列

var nums = [11, 3, 3, 4, 5, 6, 5, 1]
nums.sort()//默认升序
nums.sort(by: >)//降序

print nums: 
升序 [1, 3, 3, 4, 5, 5, 6, 11]
降序 [11, 6, 5, 5, 4, 3, 3, 1]复制代码

不可变数组升降序排列

let aar = [1, 3, 2, 4]
let newArr = aar.sorted()//默认升序
let newArr = aar.sorted(by: >)//降序

print arr:
升序和降序 [1, 3, 2, 4]

print newArr:
升序 [1, 2, 3, 4]
降序 [4, 3, 2, 1]复制代码

字典排序

let newDic = dic.sorted{ $0.0 > $1.0 }

print key:
降序 [(key: "word", value: 555), (key: "idt", value: 0), (key: "id", value: 123)]

print value:
降序 [(key: "word", value: 555), (key: "id", value: 123), (key: "idt", value: 0)]复制代码

一组数据模型的id升降序排列

struct GameInfo {
    var name: String?
    var id: Int?
    var title: String?
    var date: String?
}
var data =
        [
            GameInfo(name: "yyds", id: 0, title: "王者荣耀", date: "1632366361"),
            GameInfo(name: "letgo", id: 2, title: "王者荣耀", date: "1632366395"),
            GameInfo(name: "cc", id: 1, title: "火影忍者", date: "1632366995")
        ]   
        
let max = data.sorted { (mod0, mod1) -> Bool in
            mod0.id! < mod1.id!
}

print newData:
[["date": 1632366361, "id": 0, "name": "yyds", "title": "王者荣耀"], 
["id": 1, "title": "火影忍者", "date": 1632366995, "name": "cc"], 
["id": 2, "title": "王者荣耀", "name": "letgo", "date": 1632366395]]复制代码

sort:可以进行简单到复杂数据结构的排序。sort无返回,在原数组上修改;sorted返回有序数组,不会修改原数组

场景二 筛选查找

统计区间

计算各个分段人数,及打印分数成绩

let number = [10, 30, 50, 0 , 90, 91, 100, 98, 0]
let excellent = number.filter{ $0 <= 100 && $0 >= 90 }
let good = number.filter{ $0 == 100 }
let failed = number.filter{ $0 < 60 }
let zero = number.filter{ $0 == 0 }
        
let fraction = excellent.map { "($0)分" }
let zero_fraction = failed.map { "($0)分" }

print:
分数90以上(包括90)有 ,4人; 分别是 ["90分", "91分", "100分", "98分"]
分数100有 ,1人
分数不及格有 ,5人; 分别是 ["10分", "30分", "50分", "0分", "0分"]
分数零分有 ,2人复制代码

查找内容

找到navigationController栈中viewController,并改变对应title、backgroundColor

navigationController?.viewControllers.filter{ (vc) -> Bool in
  vc.title == "诶呀呀"
  }.map {
  $0.title = "鸽鸽"
  $0.view.backgroundColor = .green
  }复制代码

filter:用于过滤出数组中满足条件的元素,生成新的数组;同时还能在过滤条件的基础上对新数组中的元素进行映射

场景三 计算和转换

计算 - 菜场买菜

张阿姨拿了五元和陈伯伯拿了二十元去买菜,他们都要买胡萝卜和黄瓜,并且需要留一块钱坐车回家,请问张阿姨和陈伯伯各买了多少(胡萝卜1元,黄瓜2元)

let nums = ["张阿姨": 5, "陈伯伯": 20]
        
let money = nums.map { (person) -> String in
let remaining = person.value - 1
let all = remaining / 3
var carrot = 0
var cucumber = 0
if remaining % 3 == 1 {
  carrot = 1
}else if remaining % 3 == 2 {
  cucumber = 2
}
return "(person.key)买了 (all + carrot)个胡萝卜和(all + cucumber)个黄瓜"            
}

print:
["陈伯伯买了 7个胡萝卜和6个黄瓜", "张阿姨买了 2个胡萝卜和1个黄瓜"]复制代码

解多层集合效果

let arr = [[1, 2, 3],[5, 5, 5],[3, 5, 7]]
let newarr = arr.flatMap{ $0 }

print:
[1, 2, 3, 5, 5, 5, 3, 5, 7]复制代码

创建多层效果

let arr = [1, 2, 3, 4]
let abc = aar.compactMap {
  Array(repeating: $0, count: $0)
}
[[1], [2, 2], [3, 3, 3], [4, 4, 4, 4]]复制代码

map:通过对集合中的元素映射转化,返回一个泛型的数组集合;compactMap会将转换失败为nil的元素过滤掉,其它的和map相同;flatMap会改原始集合的层级结构,用来合并元素,并可以看做

所以,swift4.1以后在闭包返回可选的情况下使用compactMap,一般情况下可以使用map。

更强大的场景:reduce

我曾经遇到过这样一种情况,需要在evaluateJavaScript方法中拿到cookies并将它从最初不标准的String转化成标准的jsonString格式,我是这么做的

webView.evaluateJavaScript("document.cookie") { (cookie, error) in
                let cook = cookie as? String ?? ""
                
                let array = cook.replacingOccurrences(of: " ", with: "").components(separatedBy: ";")
                
                let units = array.reduce(into: [String: String]()) { result, key in
                    let arr = key.components(separatedBy: "=")
                    let key = arr[0]
                    var value: String?
                    value = arr[1]
                    result[key] = value ?? ""
                    
                }
                guard let pt_cookies = units.toJSONString() else {
                    return
                }
                print(pt_cookies)
            }复制代码

可以说不需要复杂的操作,不需要对字符数组复杂的提取,也不需要在字典中频繁的操作,转换过程就在内部轻易的实现了。正是使用了reduce,它满足我们对高阶函数的一切需求和向往。

reduce的两个函数

@inlinable public func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result

@inlinable public func reduce<Result>(into initialResult: __owned Result, _ updateAccumulatingResult: (inout Result, Element) throws -> ()) rethrows -> Result复制代码

主要在于nextPartialResult: (Result, Element)updateAccumulatingResult: (inout Result, Element)

一个是将遍历后调用闭包的结果赋值给临时变量,一个是遍历后,将变量的地址作为闭包的第一参数,而执行闭包就是在对变量值进行操作。

使用reduce实现map

let list = [1, 2, 3]
let s = list.reduce(into: String()) { (result, key) in
  result.append("(key)")
}

print:
"123"复制代码

使用reduce实现filter

let nums = [11, 3, 3, 4, 5, 6, 5, 1]
let reduce = nums.reduce(into: [Int]()) { (result, key) in
  if key > 4 {
    result.append(key)
  }
}

print:
[11, 5, 6, 5]复制代码

使用reduce链式组合实现sorted

let r = nums.sorted(by: >).reduce(into: [Int]()) { (result, key) in
  if key > 4 {
    result.append(key)
  }
}复制代码

上面使用的reduce函数中,into表示初始类型(可以设置默认值),闭包元祖中的第一个参数是容器,第二个参数是原始序列逐一遍历的元素。它能帮助我在有限的代码中有效的表达我的意图,使得内部结构更直接。

高阶函数与地址

var arr = [1, 2, 3, 4]
let b1 = withUnsafePointer(to: &arr) {$0}
arr.map{ $0 }
let b2 = withUnsafePointer(to: &arr) {$0}
var filter = arr.filter{ $0 < 4 }
let b3 = withUnsafePointer(to: &filter) {$0}

print:
b1 0x00007ffee289c300
b2 0x00007ffee289c300
b3 0x00007ffee3a392c0

#高阶函数并不会改变其地址


作者:睡不好的黑眼圈
链接:https://juejin.cn/post/7011319113826074654


文章分类
后端
版权声明:本站是系统测试站点,无实际运营。本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 XXXXXXo@163.com 举报,一经查实,本站将立刻删除。
相关推荐