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