当前位置: 首页 > news >正文

Swift学习笔记25-函数式编程

Array的常见操作

//Array的常见操作 //注意:Map和FlatMap都是映射高级函数 var arr = [1, 2, 3, 4] //这是这块的基础 var arr2 = arr.map { $0 * 2 } //每一个元素分别乘2 print(arr2)//[2, 4, 6, 8] //map:对数组的每个元素应用指定的函数,返回一个新数组。 //闭包:闭包中的 $0 代表当前正在处理的元素。 var arr3 = arr.filter{ $0 % 2 == 0} //筛掉了13,留下24(偶数) print(arr3)//[2, 4] //filter:根据条件筛选数组中的元素,返回一个仅包含满足条件的元素的新数组。 //$0 % 2: “将当前元素 $0 除以 2,取其余数”余数是否等于0,则是检验这个数是否是偶数 var arr4 = arr.reduce(0) { $0 + $1 } //数组内的元素依次累加,输出和 print(arr4) //reduce:将数组中的所有元素聚合成一个单一的值。 // (0)是求和的初始值 //{ $0 + $1 }是一个闭包,它接受两个参数:$0:代表累加器(当前的聚合结果),$1:代表数组中的当前元素。 // (0, +)表示把数组内的元素依次相加,且0为求和的初始值 func double(_ i: Int) -> Int { i * 2} //{ i * 2 }这也是一个闭包,闭包是一种可以捕获和存储其上下文的代码块,语法非常简洁。 var arr_double = [1, 2, 3, 4] print(arr_double.map(double))//对arr_double数组中的每个元素应用double函数,然后返回新数组 var arr_1 = [1, 2, 3] var arr_2 = arr_1.map { Array.init(repeating: $0, count: $0)} var arr_3 = arr_1.flatMap { Array.init(repeating: $0, count: $0)} print(arr_2)//[[1], [2, 2], [3, 3, 3]](因为map是默认合起来,嵌套形式的) print(arr_3)//[1, 2, 2, 3, 3, 3] //flatmap:与 map 相似,但会将生成的数组“扁平化”,即将嵌套数组合并为单一数组。 //闭包里的$0是指代数组中的元素的 //repeating:这是要重复填充的元素(这里意思就是数组里的全部元素都要重复填充)。 //count:这是数组的大小,也就是你希望创建多少个该元素(这里的意思是创建和数组的数字一样的元素)。 var arr_q = ["123", "test", "jack", "-30"] var arr_q2 = arr_q.map { Int($0) }//把数组里的每一个元素转换为整数然后再组合起来,转不成功的就会变成nil var arr_q3 = arr_q.compactMap { Int($0)}//把数组里每一个元素都转换成整数,把nil去除 print(arr_q2)//[Optional(123), nil, nil, Optional(-30)] print(arr_q3)//[123, -30] //Map:对数组的每个元素应用指定的函数,返回一个新数组。 //compactMap:与 map 相似,但会自动过滤掉结果中的 nil 值 //↓使用reduce实现map、filter的功能 var arr_1234 = [1, 2, 3, 4] print(arr_1234.map { $0 * 2 })//数组里每一个元素乘2再组成新数组[2, 4, 6, 8] print(arr_1234.reduce([]) { $0 + [$1 * 2] })//[2, 4, 6, 8] //reduce的初始值是放在()里的,这里就是一个空数组。这是一个闭包,$0表示初始情况,$1表示原数组中的每一个元素。 //表示每次循环中都会增加新的元素。 print(arr_1234.filter{ $0 % 2 == 0}) //$0代表当前元素 print(arr_1234.reduce([]) { $1 % 2 == 0 ? $0 + [$1] : $0 })//$0代表累计结果,$1代表当前元素 //$1 % 2 == 0 ?是if数组中的某个元素除以2的余数为0为真的意思,$0 + [$1]就在当前数组中加入这个元素. //[$1] 创建一个包含当前偶数元素的新数组 //: $0:表示如果 $1 不是偶数,返回当前的累积结果 $0,即不对累积结果做任何修改。

lazy的优化

//lazy 是一个关键字,用于延迟计算属性或变量的值。使用 lazy 修饰符的属性会在第一次访问时才进行计算。 let arr_123 = [1, 2, 3] let result = arr_123.lazy.map { //表示让数组里的函数全部经历闭包,result也是个数组,但是这个操作只有用到了闭包才会执行。 (i: Int) -> Int in//i其实就是数组里的元素 print("mapping \(i)")//用于查看木气正在处理哪一个元素 return i * 2 //对输入的元素乘2 }//这是一个闭包,输入参数i,然后* 2。这里是lazy,所以只有在访问lazy计算结果时,map 操作才会执行。 print("begin-----") print("mapped", result[0]) //访问第一个元素,就是i = 1的情况 //mapping 1 //mapped 2(这是return i * 2的结果) print("mapped", result[1]) //mapping 2 //mapped 4 print("mapped", result[2]) //mapping 3 //mapped 6

Optional的map和flatMap(高级映射函数)

Optional的map和flatMap:这个逻辑和数组其实是非常相似的

举例1

var num1: Int? = 10//这是一个可选项 print(num1)//Optional(10),它没有被解包 var num2 = num1.map { $0 * 2 } //闭包中的$0指代可选项包装的东西? print(num2)//Optional(20),似乎是直接把被可选项包装的东西乘了2(这是map的作用,把里面的东西都执行闭包/函数操作) /*map 函数用于对 num1 进行操作。如果 num1 不为 nil,则执行闭包中的逻辑($0 * 2),否则返回 nil。 这里 $0 表示 num1 中的值(即10)。计算结果为 20,所以 num2 变为 Optional(20)。*/ var num3: Int? = nil var num4 = num3.map { $0 * 2 } //map有一个特性,如果输入的值是nil,则不会被执行,直接返回原值。 print(num4)//nil //由于 num3 是 nil,因此闭包不会被执行,最终 num4 也会是 nil。输出为 nil

举例2

//举例2 var num_1: Int? = 10 var num_2 = num_1.map { Optional.some($0 * 2) }//这个应该是用可选项再封装了一层,整成嵌套可选项了。这个some是? print(num_2) //Optional(Optional(20)) var num_3 = num_1.flatMap { Optional.some( $0 * 2)} //some用来包装一个可选类型的一个单一值的关键词,只用于可选项。Optional.some(value) 表示该值存在且不为 nil print(num_3)//Optional(20)flatmap合并了两个Optional //这里的flatMap方法用来处理嵌套的可选类型。

举例3

var num_11: Int? = 10 var num_22 = (num_11 != nil) ? (num_11! + 10) : nil // num_11不为nil是否为真?是则执行()内容,num_22等于num_11在强制解包后+10,如果为假num_22为nil //a != b:如果 a 和 b 的值不同,则该表达式返回 true,否则返回 false print(num_22) var num_33 = num_11.map { $0 + 10 }//如果num_11不为nil则执行闭包内容,可选项里面的东西加10,出来要保留 //num_22和num_33效果是一样的,但是map简便很多

举例4

var fmt = DateFormatter() fmt.dateFormat = "yyyy-MM-dd" var str: String? = "2026-01-01" var date1 = str != nil ? fmt.date(from: str!) : nil //如果 str 不为 nil,则强制解包 str(使用 !),并调用 fmt.date(from:) 方法将字符串转换为日期。 var date2 = str.flatMap(fmt.date) //让str里的都执行fmt.date操作(只执行不为nil的)

举例5

var score: Int? = 98 var score1 = score != nil ? "socre is \(score!)" : "No score" //这段和下一段完全等价 var score2 = score.map { "score is \($0)" } ?? "No score" //用map让score执行闭包(闭包中的$0代表score的值),如果输入的是nil,则不执行闭包,直接执行??后的代码。 //?? 是 Swift 中的空合并运算符,用于简化处理可选类型,提供一个默认值,当可选类型为 nil 时使用

举例6

struct Person { var name: String var age: Int }
//举例6:结合结构体和函数使用Map和Flatmap(struct Person) var items = [ Person(name: "jack", age: 20), Person(name: "rose", age: 21), Person(name: "kate", age: 22) ]//创建一个数组,里面是一些Person的实例 //方式1:其实感觉这里不是很懂??? func getPerson1(_ name: String) -> Person? { //创建一个函数,接收一个参数name,返回的是一个Person?类型 let index = items.firstIndex { $0.name == name } //使用firstIndex方法来寻找符合闭包条件的items内的元素. //闭包里写的条件是items数组中的每个元素的name属性与参数name相等。) return index != nil ? items[index!] : nil // items[index!] 从items数组中获取索引为index!的元素,即获取找到的 Person 实例。 // 判断 index 是否为 nil,如果不为 nil,使用强制解包 index! 获取对应的Person实例并返回 // 使用数组公式(items[index!])是因为想直接从数组中根据索引抓取元素*? } //方式2:map这个简单易懂 func getPerson2(_ name: String) -> Person? { return items.firstIndex { $0.name == name }.map { items[$0] } }//同方式1,使用 firstIndex 查找方法来获取符合条件的索引 //使用 map 方法:如果找到了索引(firstIndex不为nil),则map函数会获取该索引items[$0],并返回对应的 Person 实例。$0感觉就是指代上下文中需要传递的东西的,确实。 //闭包参数:定义参数来接收外部传递的值。隐式参数名称:如果你没有明确为闭包定义参数名称,Swift 会提供隐式的参数名称,如 $0、$1 等。$0表示闭包的第一个参数。$1表示闭包的第二个参数。

举例7

struct Person_1 { var name: String var age: Int init?(_ json: [String : Any]) { //可失败初始化器(可返回nil).初始化这个结构体,参数是一个字典名为json // 字典的键是 String 类型,值是 Any 类型(可以是任意类型)。 guard let name = json["name"] as? String, //把name转换成字符串,如果可以转 let age = json["age"] as? Int else {//就再把age转成Int return nil//如果不行,就返回nil } self.name = name //只有在 guard 中的条件都满足后,才会执行到这两行。 self.age = age } } //guard用于确保特定条件为真,如果条件不成立,则执行某些替代操作或提前返回
//举例7:flatMap可以用于初始化,Optional的字典里使用map和flatMap(struct: Person_1) //!!!这是使用flatMap的新方式 var json: Dictionary? = ["name" : "Jack", "age" : 10]//创建一个变量json它的数据类型是字典(可选) //↓这是用于初始化Person的旧方式 var p1 = json != nil ? Person_1(json!) : nil //创建一个变量,如果json不是nil就返回强制解包后的json,将json作为参数传入Person_1。否则返回nil print(p1)//Optional(class25_FuncProgramming.Person_1(name: "Jack", age: 10)) var p2 = json.flatMap(Person_1.init) /*Person_1.init:这是Person_1结构体的初始化方法。 它接收一个字典参数,如果该参数有效并能成功创建Person_1对象,则返回类型为Person_1,否则返回 nil。*/ //flatMap:用于对可选值执行函数并返回一个可选值。这里,它将 json 传递给 Person_1.init 方法。 //如果 json 为 nil,flatMap 会直接返回 nil,如果有值,将调用Person_1的初始化方法

函数式编程 主面试:太复杂,不符合现阶段

函数式编程(FP)(Functional Programming):是一种编程范式,大概就是规范吧。旨在把复杂的计算过程分解成一系列可复用函数的调用。

函数可以赋值给其他变量,也可以作为函数参数和函数返回值传递。

历史:最早出现在LISP语言,绝大多数语言都支持这个,比如JavaSript, Swift, Python, HasKell等等

常用概念:Higher-Order Function(高阶函数), Function Currying(柯里化)

Functor, Applicative Functor, Monad

FP实践-传统写法

假设要实现以下功能:[(num + 3) * 5 - 1] % 10 / 2

在Swift等语言中,%运算符表示取模运算(取余数运算)。具体来说,表达式 a % b 返回 a 除以 b 的余数。

原始写法

这里是设定好几个函数,然后依次把参数传进去:

var num_01 = 1 func add_01(_ v1: Int, _ v2: Int) -> Int{ v1 + v2 } func sub_01(_ v1: Int, _ v2: Int) -> Int{ v1 - v2 } func multiple_01(_ v1: Int, _ v2: Int) -> Int{ v1 * v2 } func divide_01(_ v1: Int, _ v2: Int) -> Int{ v1 / v2 } func mod_01(_ v1: Int, _ v2: Int) -> Int{ v1 % v2 } divide_01(mod_01(sub_01(multiple_01(add_01(num_01, 3), 5), 1), 10), 2)

函数式写法

//函数式写法 func add_02(_ v: Int) -> (Int) -> Int{ {$0 + v } } func sub_02(_ v: Int) -> (Int) -> Int{ { $0 - v } } func multiple_02(_ v:Int) -> (Int) -> Int{ {$0 * v } } func divide_02(_ v: Int) -> (Int) -> Int{ { $0 / v } } func mod_02(_ v: Int) -> (Int) -> Int{ { $0 % v } } infix operator >>> : AdditionPrecedence //自定义了>>>运算符:这个AdditionPrecedence是优先级的意思,没括号的时候用 func >>><A, B, C>(_ f1: @escaping (A) -> B, _ f2: @escaping (B) -> C) -> (A) -> C {{ f2(f1($0))}} //var fn_02 = add_02(3) >>> multiple_02(5) >>> sub_02(1) >>> mod_02(10) >>> divide_02(2) //这种没有人用的,傻x才用,运行不了也不管了 //fn_02(num_01)

这种没有人用的,傻x才用,运行不了也不管了

柯里化(Currying):将一个接收多参数的函数转换成一系列只接受单个参数的函数

在柯里化中,每次调用函数我们都只提供一个参数,这样做的好处是我们可以“一步一步”地提供参数,而不需要一次性提供全部。

也可以用于参数复用:如果我们有多个不同的操作需要使用相同的参数,可以避免重复输入。

感觉这个有点麻烦和复杂,不是特别懂...

举例1:

//举例: func add(_ v1: Int, _ v2: Int) -> Int { v1 + v2 } add(10, 20) func curriedaff(_ v: Int) -> (Int) -> Int { { $0 + v } }//$0指代前面的结果,v其实是另个参数 curriedaff(10)(20)//10是第一个参数,20是第二个 //这个函数首先接受一个参数 v,返回一个接收另一个参数的闭包。

举例2:

func add1(_ v1: Int, _ v2: Int) -> Int { v1 + v2 } func add2(_ v1: Int, _ v2: Int, _ v3: Int) -> Int { v1 + v2 + v3 } func currying<A, B, C>(_ fn: @escaping(A, B) -> C) -> (B) -> (A) -> C { { b in { a in fn(a, b) } } } func currying<A, B, C, D>(_ fn: @escaping(A, B, C) -> D) -> (C) -> (B) -> (A) -> (D) { { c in { b in {a in fn(a, b, c) }}} } //因为在外面(这里是闭包里)还要调用fn,所以要用@escaping let curriedAdd1 = currying(add1) print(curriedAdd1(10)(20))//30 let curriedAdd2 = currying(add2) print(curriedAdd2(10)(20)(30))//分别传参数进去,不用一次性传全部 60 //<A, B, C> 是 Swift中的泛型(Generics)语法,用于定义一个通用的函数、类型或结构,能够处理不同的数据类型。具体来说,这些字母代表占位符,可以在使用该函数或类型时传入具体的类型。

举例3

func add_1(_ v1: Int, _ v2: Int) -> Int { v1 + v2 } func sub(_ v1: Int, _ v2: Int) -> Int { v1 - v2 } func multiple(_ v1: Int, _ v2: Int) -> Int { v1 * v2 } func divide(_ v1: Int, _ v2: Int) -> Int{ v1 / v2 } func mod(_ v1: Int, _ v2: Int) -> Int { v1 % v2 } prefix func ~<A, B, C>(_ fn: @escaping (A, B) -> C) -> (B) -> (A) -> C { { b in { a in fn( a,b ) } } } infix operator >>> : AdditionPrecedence func >>><A, B, C>(_ f1: @escaping (A) -> B, _ f2: @escaping (B) -> C) -> (A) -> C { { f2(f1($0)) } } var num = 1 //var fn = (~add_1)(3) >>> (~multiple)(5) >>> (~sub)1 >>> (~mod)(10) >>> (~divide)(2) //fn(num) //鬼知道为什么报错,不懂。

函子和适用函子:略。

http://www.jsqmd.com/news/657841/

相关文章:

  • 宝塔面板实战:从零部署Python Web应用
  • GitHub Copilot ≠ 生产就绪:团队落地智能代码生成必须跨过的4道合规与质量关卡
  • 生成式AI落地不是技术问题,而是组织能力缺口(SITS2026独家“AI就绪度”评估矩阵首次发布)
  • 【12.MyBatis源码剖析与架构实战】15.1 if和where标签执⾏过程剖析-初始化时
  • 从GKCTF 2021 XOR题解看异或运算在密码学中的巧妙应用与比特爆破实战
  • 从冠军方案拆解:在Jane Street预测赛中,如何用AE+MLP+XGBoost玩转模型融合?
  • AI辅助排版:设计领域的应用方法与落地实践
  • 西门子S7-1200 PLC控制三相六拍步进电机:从梯形图到实物接线保姆级教程
  • 旧显示器秒变智能投屏屏!树莓派4B双协议(Miracast+AirPlay)无线投屏器完整配置指南
  • 如何三步解锁WeMod Pro功能:Wand-Enhancer终极指南
  • 别再让Copilot绕过你的Security Gate!:实时拦截高危生成代码的eBPF+LLM Guard联合审查方案(已通过ISO 27001渗透验证)
  • FastGPT 架构深度分析
  • STM32新手必看:GPIO初始化失败,别再用RCC_AHBPeriphResetCmd了!
  • 不止于分词:用SpringBoot+HanLP 1.7.7快速构建一个简易文本分析服务
  • 数据库基础概念与体系结构 - 软考备战(二十九)
  • Tiny-ViT: A Compact Vision Transformer for Efficient and Explainable Potato Leaf Disease Classificat
  • 011、算子中间表示概述:计算图与算子抽象
  • YOLO+ByteTrack路口违章抓拍实战:多目标稳定追踪与违章判定
  • 2026年软件测试工具TOP 10选型指南:趋势洞察与实战决策
  • Android音频调试实战:用dumpsys media.audio_flinger揪出音频卡顿的元凶
  • 如何把MAX31865的精度榨干?STM32驱动PT100三线制测温的校准与优化实战
  • 多SKILL协同推理:双慢病联合决策:SKILL架构下糖尿病与高血压的协同诊疗体系.147
  • 新能源汽车整车控制器VCU学习模型:初学者的快速入门指南
  • 智能代码生成风格一致性落地指南(2024企业级实践白皮书)
  • 012、张量与数据布局:内存模型与对齐策略
  • 从Urbannav真值话题到NavSatFix:手把手教你转换GPS数据格式用于ROS定位评估
  • 2026最权威的AI科研网站推荐
  • 智能排版:核心功能解析与效率提升实践指南
  • Java雪花算法实战:从原理剖析到高并发场景下的ID生成器实现
  • 保姆级教程:用Python和COCO API搞定MSCOCO数据集下载、解析与可视化