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

2024-04-22 06:28

本文主要是介绍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

这篇关于Swift-25-普通函数、闭包函数与Lamda表达式编程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/925021

相关文章

Python itertools中accumulate函数用法及使用运用详细讲解

《Pythonitertools中accumulate函数用法及使用运用详细讲解》:本文主要介绍Python的itertools库中的accumulate函数,该函数可以计算累积和或通过指定函数... 目录1.1前言:1.2定义:1.3衍生用法:1.3Leetcode的实际运用:总结 1.1前言:本文将详

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

MySQL数据库函数之JSON_EXTRACT示例代码

《MySQL数据库函数之JSON_EXTRACT示例代码》:本文主要介绍MySQL数据库函数之JSON_EXTRACT的相关资料,JSON_EXTRACT()函数用于从JSON文档中提取值,支持对... 目录前言基本语法路径表达式示例示例 1: 提取简单值示例 2: 提取嵌套值示例 3: 提取数组中的值注意

C#多线程编程中导致死锁的常见陷阱和避免方法

《C#多线程编程中导致死锁的常见陷阱和避免方法》在C#多线程编程中,死锁(Deadlock)是一种常见的、令人头疼的错误,死锁通常发生在多个线程试图获取多个资源的锁时,导致相互等待对方释放资源,最终形... 目录引言1. 什么是死锁?死锁的典型条件:2. 导致死锁的常见原因2.1 锁的顺序问题错误示例:不同

Java function函数式接口的使用方法与实例

《Javafunction函数式接口的使用方法与实例》:本文主要介绍Javafunction函数式接口的使用方法与实例,函数式接口如一支未完成的诗篇,用Lambda表达式作韵脚,将代码的机械美感... 目录引言-当代码遇见诗性一、函数式接口的生物学解构1.1 函数式接口的基因密码1.2 六大核心接口的形态学

PyCharm接入DeepSeek实现AI编程的操作流程

《PyCharm接入DeepSeek实现AI编程的操作流程》DeepSeek是一家专注于人工智能技术研发的公司,致力于开发高性能、低成本的AI模型,接下来,我们把DeepSeek接入到PyCharm中... 目录引言效果演示创建API key在PyCharm中下载Continue插件配置Continue引言

使用C#代码计算数学表达式实例

《使用C#代码计算数学表达式实例》这段文字主要讲述了如何使用C#语言来计算数学表达式,该程序通过使用Dictionary保存变量,定义了运算符优先级,并实现了EvaluateExpression方法来... 目录C#代码计算数学表达式该方法很长,因此我将分段描述下面的代码片段显示了下一步以下代码显示该方法如

五大特性引领创新! 深度操作系统 deepin 25 Preview预览版发布

《五大特性引领创新!深度操作系统deepin25Preview预览版发布》今日,深度操作系统正式推出deepin25Preview版本,该版本集成了五大核心特性:磐石系统、全新DDE、Tr... 深度操作系统今日发布了 deepin 25 Preview,新版本囊括五大特性:磐石系统、全新 DDE、Tree

Oracle的to_date()函数详解

《Oracle的to_date()函数详解》Oracle的to_date()函数用于日期格式转换,需要注意Oracle中不区分大小写的MM和mm格式代码,应使用mi代替分钟,此外,Oracle还支持毫... 目录oracle的to_date()函数一.在使用Oracle的to_date函数来做日期转换二.日

什么是 Ubuntu LTS?Ubuntu LTS和普通版本区别对比

《什么是UbuntuLTS?UbuntuLTS和普通版本区别对比》UbuntuLTS是Ubuntu操作系统的一个特殊版本,旨在提供更长时间的支持和稳定性,与常规的Ubuntu版本相比,LTS版... 如果你正打算安装 Ubuntu 系统,可能会被「LTS 版本」和「普通版本」给搞得一头雾水吧?尤其是对于刚入