Swift-25-普通函数、闭包函数与Lamda表达式编程

函数

语法定义

先来看下swift中函数的定义,函数用关键字func来指定,语法相对复杂一点,主要有下列4种基本情况,还有比较复杂的,会在后续详细讲解。

无参函数定义

在这里插入图片描述

有参函数定义

在这里插入图片描述

一个简单的函数和函数调用示例如下:

    func printGreeting1() {print("Hello, playground.")}printGreeting1()func printGreeting2() ->String {return "Hello, playground.";}lef str = printGreeting2()

函数参数

函数有参数(parameter)之后就能做更多的事情了。利用参数可以向函数输入数据。我们之所以把函数的这部分称为“参数”,是因为它们可以根据调用者给函数传递的数据来改变自己的值。函数利用传递给自己的参数来执行任务或产生结果。 创建一个函数,利用参数打印更加个性化的问候信息,代码如下所示:

  //带一个参数func printPersonalGreeting(name: String) {print("Hello \(name), welcome to your playground.")}printPersonalGreeting(name: "Matt")

参数内部名称

语法格式为:paramName:ParamType,前面是参数名称,后面指参数类型。

      //带两个参数func divisionDescriptionFor(numerator: Double, denominator: Double) {print("\(numerator) divided by \(denominator) equals \(numerator / denominator)")}divisionDescriptionFor(numerator: 9.0, denominator: 3.0)

参数外部名称

语法格式为:outParName inParName:ParamType,依次表示外部调用时的参数名称,函数内部使用时的参数名称,参数类型。

这主要是为了代码的可读性更强而设计的,并没其它实际意义,但在调用时名称要匹配,如下例为外部调用重新声了一个名为to的参数名称以专让供外部调用使用。

func printPersonalGreeting(to name: String) { 13 print("Hello \(name), welcome to your playground.")
}
printPersonalGreeting(to: "Matt")

变长参数

语法格式为:paramName:ParamType... 注意最后面的三个点,就是变长参数的特殊关键字。

函数只能有一个变长参数,而且一般应该是函数参数列表中的最后一个。参数值在函数内是以数组的形式使用,比如下例所示:

func printPersonalGreetings(to names: String...) {for name in names {print("Hello \(name), welcome to the playground.")}
}
printPersonalGreetings(to: "Alex","Chris","Drew","Pat")

在这里插入图片描述

参数默认值

语法格式为:paramName:ParamType=value 用=号进行默认值赋值操作。

默认值应该放在函数参数数列表的末尾,如果形参有默认值,那么在调用函数时可以省略实参。

func divisionDescriptionFor(numerator: Double,denominator: Double, withPunctuation punctuation: String = ".") -> String { //默认值设置return "\(numerator) divided by \(denominator) equals \(numerator / denominator)\(punctuation)"
}//9.0 divided by 3.0 equals 3.0.  少传一个参数
print(divisionDescriptionFor(numerator: 9, denominator: 3))
//9.0 divided by 3.0 equals 3.0!  替换默认参数
print(divisionDescriptionFor(numerator: 9, denominator: 3, withPunctuation: "!"))

in-out 参数

语法格式为:paramName:input ParamType ,这里的input是一个关键字。

函数有时候需要修改实参的值。in-out(in-out parameter)能让函数影响函数体以外的变量。有两个注意事项:

  • 首先,in-out不能有默认值;
  • 其次,变长参数不能标记为inout;

in-out的作用其实就是用于实参值的修改,省去return的写法,也可认为是达到多返回的目的。

var error = "The request failed:"
//_是一个外部函数名,它有特殊含义,即如果定义外部参数名为_,则在函数被调用时可以不写参数名称
func appendErrorCode(_ code: Int, toErrorString errorString: inout String) {if code == 400 {errorString += " bad request."}
}//用inout修饰时,调用时需要加上&,表示函数会修改这个变量,此段代码运行最后error的值会被改写成The request failed: bad request. 这有点像return干的事。
appendErrorCode(400, toErrorString: &error)
error

函数返回值

函数结束执行后可以返回一些信息,这些信息称为函数的返回值,用return关键字来标识。

单个返回值

一个简单的例子,如下

func divisionDescriptionFor() -> String {return "numerator"
}var v = divisionDescriptionFor();

多个返回值

语法结构 : ->(name1:[DataType], name2:[DataType]),前面的name1和name2也可以省略,但不建议。

函数可以返回不止一个值。Swift用元组数据类型来做到这一点。

func sortEvenOddNumbers(_ numbers: [Int]) -> (evens: [Int], odds: [Int]) {var evens = [Int]()var odds = [Int]()for number in numbers {if number % 2 == 0 {evens.append(number)} else {odds.append(number)}}return (evens, odds)
}
let aBunchOfNumbers = [10,1,4,3,57,43,84,27,156,111]
let theSortedNumbers = sortEvenOddNumbers(aBunchOfNumbers)//The even numbers are: [10, 4, 84, 156]; the odd numbers are: [1, 3, 57, 43, 27, 111]
print("The even numbers are: \(theSortedNumbers.evens); the odd numbers are: \(theSortedNumbers.odds)")//一个更复杂的例子
func siftBeans(fromGroceryList list: [String]) -> (beanCount: Int, beansBought: [String]) 

多返回值运算

func +(lhs: Point, rhs: Point) -> Point {let newX = (lhs.x + rhs.x)let newY = (lhs.y + rhs.y)return Point(x: newX, y: newY)
}let p1 = Point(x: 1, y: 2)
let p2 = Point(x: 3, y: 5)let p3 = p1 + p2 //(4,7)
p3.x
p3.y

可空类型返回

某些情况下希望函数可返回一个可空实例(用?或!来标识的返回值),主要用于一个函数在某些情况下返回nil,在其它情况下返回一个有意义的实例值的场景

//此函数只有一个参数,参数为元组类型,其中元组的第二个值可有可无的
func grabMiddleName(fromFullName name: (String, String?, String)) -> String? {return name.1 //元组的索引,表示取参数中元组的第二个值,索引从0开始
}//因为函数调用时第二个元组值为nil,所以不会打印任何信息
let middleName = grabMiddleName(fromFullName: ("Matt",nil,"Mathias"))
if let theName = middleName {print(theName)
}

函数返回与中断

主要是用return语句,但这里用了一个特殊的语法guard,熟悉下其用法就可以了。

func greetByMiddleName(name: (first: String, middle: String?, last: String)) {//把middle的值绑定到middleName常量上guard let middleName = name.middle else {print("Hey there!")return}print("Hey \(middleName)!")
}greetByMiddleName(name: ("Matt","Danger","Mathias"))

嵌套函数

Swift的函数定义可以嵌套。嵌套函数在另一个函数定义的内部声明并实现。嵌套函数在包围它的函数以外不可用。当需要一个函数只在另一个函数内部做一些事情时,这个特性很有用。

func areaOfTriangleWith(base: Double, height: Double) -> Double {let numerator = base * height//嵌套函数func divide() -> Double {return numerator / 2}return divide()
}
areaOfTriangleWith(base: 3.0, height: 5.0) //~~ 7.5

声明函数型变量

主要是把函数定义为一个普通变量,这样就可以把函数做为参数传递了,比如下面代码的实现,就是为了把sortEvenOddNumbers做为参数传递。

func sortEvenOddNumbers(_ numbers: [Int]) -> (evens: [Int], odds: [Int]) {return (evens, odds)
}//创建了一个evenOddFunction常量,其值是sortedEvenOddNumbers(_:)函数
let evenOddFunctionType: ([Int]) -> ([Int], [Int]) = sortEvenOddNumbers

闭包

闭包是在应用中完成特定任务的互相分离的功能组,类似于函数但省去了命名和声明,相比函数而言会比较轻量化。先举一个子来一个直观的感受,下面是一个数组排序的例子:

let volunteerCounts = [1,3,40,32,2,53,77,13]func sortAscending(_ i: Int, _ j: Int) -> Bool {return i < j
}
//~~ [1, 2, 3, 13, 32, 40, 53, 77]
let volunteersSorted = volunteerCounts.sorted(by: sortAscending)

如果采用闭包,则代码可以更简单,如下所示:

let volunteerCounts = [1,3,40,32,2,53,77,13]
let volunteersSorted = volunteerCounts.sorted(by: {(i: Int, j: Int) -> Bool inreturn i < j
})

语法结构

基础语法如下例:

{ (parameters) -> return type in // 代码
}
  • 闭包整体代码写在 {} 中;
  • (parameters) 是参数部分;
  • -> return type 表示返回值
  • in 是一个关键字,用来分隔前两部分与代码的实现;

上面的例子还可以再简化:

//简化版本1
let volunteersSorted = volunteerCounts.sorted(by: { i, j in i < j })//简化版本2:$0代表第一个参数,$1代表第二个参数,依此类推,称为快捷语法参数
let volunteersSorted = volunteerCounts.sorted { $0 < $1 }

闭包的写法虽然可以简单再简单,但需要和可读性之间做下权衡,个人还是比较建议用第1种写法,这也是大多数语言的实现方式。

闭包做为函数返回值

一般应用于函数的返回值为另一个函数的场景。下面的makeTownGrand函数返回值为一个函数,注意第一个->后面的(Int, Int) -> Int 其实是一个闭包的写法。

func makeTownGrand() -> (Int, Int) -> Int {func buildRoads(byAddingLights lights: Int, toExistingLights existingLights: Int) -> Int {return lights + existingLights}return buildRoads
}
var stoplights = 4
//此处townPlanByAddingLightsToExistingLights是一个函数变量
let townPlanByAddingLightsToExistingLights = makeTownGrand()
stoplights = townPlanByAddingLightsToExistingLights(4, stoplights)
print("Town has \(stoplights) stop lights.") //Town has 8 stop lights.

闭包做为函数参数

一般应用于函数的参数为另一个函数的场景。下成示例中makeTownGrand函数的第二个参数为一个函数。

func makeTownGrand(withBudget budget: Int, condition: (Int) -> Bool) //第二个参数为一个函数-> ( (Int, Int) -> Int )? { //返回值为函数if condition(budget) {func buildRoads(byAddingLights lights: Int, toExistingLights existingLights: Int) -> Int {return lights + existingLights}return buildRoads} else {return nil}
}//本例子中做为函数参数传统
func evaluate(budget: Int) -> Bool {return budget > 10_000
}var stoplights = 4
//evaluate函数为第二个参数
if let townPlanByAddingLightsToExistingLights = makeTownGrand(withBudget: 1_000, condition: evaluate) {stoplights = townPlanByAddingLightsToExistingLights(4, stoplights)
}if let newTownPlanByAddingLightsToExistingLights = makeTownGrand(withBudget: 10_500, condition: evaluate) {stoplights = newTownPlanByAddingLightsToExistingLights(4, stoplights)
}//~~ Town has 8 stop lights.
print("Town has \(stoplights) stop lights.")

捕获闭包函数内部的变量

闭包和函数能记录在其闭合作用域中定义的变量所封装的内部信息。下面的makePopulationTracker函数的返回值为一函数类型。

func makePopulationTracker(forInitialPopulation population: Int) -> (Int) -> Int {var totalPopulation = populationfunc populationTracker(growth: Int) -> Int {totalPopulation += growthreturn totalPopulation}return populationTracker
}var currentPopulation = 5_422//growBy为一函数
let growBy = makePopulationTracker(forInitialPopulation: currentPopulation) //返回函数(Int) -> Int 
growBy(500) //5922
growBy(500) //6422
growBy(500) //6922
currentPopulation = growBy(500) //7422

再往下写点代码,前两行代码要说明的是闭包是引用类型,当把函数赋给一个常量或变量时,实际上是一个指针引用,并不是副本。但后面两行代码如果通过新的变量指向这个函数,则其作用域变会改变了。

let anotherGrowBy = growBy
anotherGrowBy(500) //7922var bigCityPopulation = 4_061_981
let bigCityGrowBy = makePopulationTracker(forInitialPopulation: bigCityPopulation) //7422bigCityPopulation = bigCityGrowBy(10_000) //4071981

lamda函数式编程

Swift的函数式编程和java差不太多。简单来讲lamda表达式,使代码更简洁,但也更难维护。闭包是实现函数编程的基础,因为函数式编程要求闭包函数可以和其它基本数据类型一样,可作为返回值从别的函数返回,也可以作为参数传递给别的函数,还可以存储在变量中。

map( _: )映射

遍历集合,实现原始数据的变换操作

let precinctPopulations = [1_244, 2_021, 2_157]
let projectedPopulations = precinctPopulations.map {(population: Int) -> Int inreturn population * 2
}
projectedPopulations //[2488, 4042, 4314]

filter( _: )筛选

遍历集合,实现原始数据的筛选

let precinctPopulations = [2488, 4042, 4314]
let bigProjections = projectedPopulations.filter {(projection: Int) -> Bool inreturn projection > 4_000
}
bigProjections //[4042, 4314]

reduce( _ : _ : )统计

遍历集合,实现原始数据的累加统计

let precinctPopulations = [2488, 4042, 4314]
let totalProjection = projectedPopulations.reduce(0) {(accumulatedProjection: Int, precinctProjection: Int) -> Int inreturn accumulatedProjection + precinctProjection
}
totalProjection //10844

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/636007.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

打印机扫描到共享文件夹教程(Win系统和Mac系统)

一&#xff0e;Windows系统扫描文件到共享文件夹。 1.同时按下键盘WinR键&#xff0c;输入control&#xff0c;点击确定。 2.点击类别&#xff0c;点击大图标&#xff0c;点击凭据管理器。 3.点击Windows凭据&#xff0c;点击添加Windows凭据。 4.internet地址或网络地址&…

【Godot4自学手册】第三十九节利用shader(着色器)给游戏添加一层雾气效果

今天&#xff0c;主要是利用shader给游戏给地宫场景添加一层雾气效果&#xff0c;增加一下气氛&#xff0c;先看一下效果&#xff1a; 一、新建ParallaxBackground根节点 新建场景&#xff0c;根节点选择ParallaxBackground&#xff0c;命名为Fog&#xff0c;然后将该场景保…

循环神经网络(RNN):概念、挑战与应用

循环神经网络&#xff08;RNN&#xff09;&#xff1a;概念、挑战与应用 1 引言 1.1 简要回顾 RNN 在深度学习中的位置与重要性 在深度学习的壮丽图景中&#xff0c;循环神经网络&#xff08;Recurrent Neural Networks&#xff0c;RNN&#xff09;占据着不可或缺的地位。自从…

视频质量评价 SSIM 算法详细介绍

SSIM SSIM(Structural Similarity Index Measure)是一种用于衡量两幅图像之间相似度的指标,是属于全参考视频质量评价算法范畴;它在图像质量评估领域得到了广泛的应用。SSIM是基于人类视觉系统的特性设计的,它考虑了图像的亮度、对比度和结构信息。SSIM的值范围在-1到1之…

Visual Studio2010源码编译curl_7_60

一、源码解压目录内容 很开心里面可以找到CMakeLists.txt文件&#xff0c;说明可以实用CMake工具进行构建&#xff0c;由于多数开源项目都选择实用CMake作为构建编译工具&#xff0c;大家蝇该都比较熟练了。 二、实用CMake开始构建Visual Studio 2010工程 很顺利整个构建过程没…

el-select下拉框远程搜索且多选时,编辑需要回显的一个简单案例

前端业务开发中不管使用vue2~3&#xff0c;还是react&#xff0c;angular各种前端技术栈&#xff0c;经常会遇到这种业务。一个下拉框Select中&#xff0c;不仅需要需要支持远程模糊搜索&#xff0c;还需要支持多选。并且在编辑时&#xff0c;还能正常把已经多选好的内容回显到…

中颖51芯片学习7. printf重定向到串口与自定义日志输出函数

中颖51芯片学习7. printf重定向到串口与自定义日志输出函数 一、 printf 重定向1. 概念2. 实现方式3. C51 中printf数值格式化 二、日志函数1. 实现方案分析2. 代码&#xff08;1&#xff09;log_utils.h&#xff08;2&#xff09;main.c 3. 通过预定义宏实现日志分级输出&…

列表控件列表表格树

QListWidget QListWidget 是 Qt 框架中的一个部件&#xff0c;用于在图形用户界面中显示一个列表。这个列表可以包含文本项、图标或者其他自定义的部件。它非常适合用于呈现一系列可选择的元素。 基本属性和设置 NoSelection&#xff1a;不允许选择。用户无法选择任何项。 S…

[论文阅读链接]

CVPR2023&#xff1a;Learning Human-to-Robot Handovers from Point Clouds http://t.csdnimg.cn/OfSnShttp://t.csdnimg.cn/OfSnS仿真工具&#xff1a;dm_control: Software and Tasks for Continuous Control dm_control 翻译: Software and Tasks for Continuous Control…

政安晨:【Keras机器学习示例演绎】(六)—— 通过 CT 扫描进行 3D 图像分类

目录 简介 设置 下载 MosMedData&#xff1a;胸部CT扫描与COVID-19相关发现 加载数据和预处理 建立训练和验证数据集 数据增强 定义 3D 卷积神经网络 训练模型 模型性能可视化 通过一次 CT 扫描进行预测 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍…

Python --- 在python中安装NumPy,SciPy,Matplotlib以及scikit-learn(Windows平台)

在python中安装NumPy&#xff0c;SciPy&#xff0c;Matplotlib以及scikit-learn(Windows平台) 本文是针对(像我一样的)python新用户所写的&#xff0c;刚刚在电脑上装好python之后&#xff0c;所需的一些常见/常用的python第三方库/软件包的快速安装指引。包括了这些常用安装包…

Web后端-请求响应

黑马程序员JavaWeb开发教程 文章目录 一、请求1、简单参数2、实体参数3、数组集合参数&#xff08;1&#xff09;数组参数&#xff08;2&#xff09;集合参数 4、日期参数5、json参数&#xff08;1&#xff09;在Postman中怎么发起请求来传递JSON格式的请求参数&#xff08;2&a…