Swift学习笔记八
函数
Swift的函数语法非常独特,也提供了很高的灵活性和可读性。它可以充分表达从简单的无参数C风格函数到复杂的拥有局部变量和外部变量的OC风格的方法。参数可以有默认值,方便函数的调用。Swift中的每个函数都有一个类型,由其参数类型和返回值类型组成,这个类型可以像Swift中的任何其他类型一样被使用,因此,函数被作为参数传递,或者从一个函数返回一个函数等。函数也可以嵌套函数形成嵌套链。
定义和调用函数
定义函数时,可以给它定义一个或多个参数和返回值类型,这些并不是必须的。比如:
func sayHello(personName: String) -> String {
let greeting = "Hello, " + personName + "!"
return greeting
}
如果函数不需要返回值,那么“->type”就可以省略掉,其实此时函数仍然返回了一个特殊Void类型的值,它是一个空的元组(tuple)。
函数也可以同时返回多个值,这就是返回一个包含多个值的元组。比如:
func minMax(array: [Int]) -> (min: Int, max: Int) {
var currentMin = array[]
var currentMax = array[]
for value in array[..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
因为函数返回的元组的元素是已经定义了名称的,因此可以通过点语法来获取它的元素,比如minMax([1,2,3]).min。在函数体里边返回元组的时候,不需要标明元素的名字,因为这些名字在函数定义的时候就已经给出了。
返回可选元组类型
如果返回的元组可能存在整个元组都没有值,则可使用可选元组返回类型(optional tuple return type)来表明这个返回值可能是nil。这种类型可以写为:(Int,Int)?等,比如:
func minMax(array: [Int]) -> (min: Int, max: Int)? { //注意这里的返回值类型
if array.isEmpty { return nil }
var currentMin = array[]
var currentMax = array[]
for value in array[..<array.count] {
if value < currentMin {
currentMin = value
} else if value > currentMax {
currentMax = value
}
}
return (currentMin, currentMax)
}
之后,就可以用可选绑定来确定该函数的返回值是否有值了:
if let bounds = minMax([, -, , , , ]) {
println("min is \(bounds.min) and max is \(bounds.max)")
}
// prints "min is -6 and max is 109”
函数参数名称
函数的参数都可以指定名称,然后就可以在函数体里使用这些名称了:
func someFunction(parameterName: Int) {
// function body goes here, and can use parameterName
// to refer to the argument value for that parameter
}
不过,这里的参数名只能在函数体内部使用,所以又称之为局部参数名称(local parameter name)。这些名称可以用来在表示传入参数的意义。如果希望函数调用者在调用函数时提供参数名,就可以在局部参数名称之外再定义外部参数名称(external parameter name)。它的语法是在局部参数名称前写上外部参数名称,然后用一个空格将这两个名称隔开:
func someFunction(externalParameterName localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}
注意:如果你提供了外部参数名称,那么在调用函数的时候,必须每次都使用外部参数名称。
func join(s1: String, s2: String, joiner: String) -> String {
return s1 + joiner + s2
}
join("hello", "world", ", ") //对比
func join(string s1: String, toString s2: String, withJoiner joiner: String)
-> String {
return s1 + joiner + s2
}
join(string: "hello", toString: "world", withJoiner: ", ")
这种方式提供名称的灵活性,在调用函数的时候是以一种更清晰的语义化方式传递参数的,而在函数内部,则可以使用比较简洁的方式来标识参数。大大提高了代码的可读性。
如果你的局部参数名称已经定义了,又想把它也作为外部参数名称,那么可以使用简写语法而不需要把名称重写一遍,形式是在参数名称之前加上#符号。比如:
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}
默认参数值
作为函数定义的一部分,你可以为参数设置默认值,如果参数具备默认值,那么在调用函数的时候可以省略该参数。
在设定参数默认值时,最佳实践是将具有默认值的参数放置在参数列表的最后,这样可以确保在多次函数调用的时候,不具备默认值的参数顺序都是保持一致的,这样就可以确定这些多次调用是调用的同一个函数。设定参数默认值的语法如下:
func join(string s1: String, toString s2: String,
withJoiner joiner: String = " ") -> String {
return s1 + joiner + s2
}
可以看出,如果为参数提供了默认值,那么最佳实践就是总是为设定了默认值的参数设定外部参数名称,这就使得在函数调用的时候,不能省略这个参数,保持了函数调用方式的一致性和可读性,为了简便,Swift提供了便捷的特性,就是给所有设定了默认值的参数自动加上了外部参数名称,这个外部参数名称和其局部参数名称是相同的,就相当于前面所讲的(#)符号,这就使得设定了默认值的参数在被调用时也必须显示地给出:
func join(s1: String, s2: String, joiner: String = " ") -> String {
return s1 + joiner + s2
} join("hello", "world", joiner: "-")//这里必须显示给定joiner
// returns "hello-world”
这里在调用函数的时候,如果你实在不想写给定了默认值的参数名,那么可以用下划线(_)代替显示给定的外部名称,不过并不推荐这样做。
可变参数
一个可变参数接收0个或多个某个指定类型的值。使用可变参数意味着在调用该函数时可以向该参数传递可变数目的值。可变参数的声明语法是在参数的类型名后边加上三个点(...),向可变参数传递的值在函数体内部被组合成了一个特定类型的数组,比如:
func arithmeticMean(numbers: Double...) -> Double {
var total: Double =
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(, , , , )
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
注意:一个函数最多只能有一个可变参数,并且它必须位于参数列表的最后,这是为了避免在函数调用的时候多参数时的混淆不清。如果函数有一个或者多个指定默认值的参数,同时又有可变参数,那么把可变参数放置在所有指定默认值的参数后边,即列表的最后边。
常量和变量参数
函数的参数默认都是常量,在函数内部尝试改变参数的值会触发编译错误,这使得误操作改变参数值不会发生。
但是,有时候需要函数有一个参数的变量副本,通过指定一个或多个参数为变量参数,就不用再在函数里边重新定义变量了。变量参数是作为变量而非常量使用,它提供了一个可改变的值副本来供你的函数操作。
通过在参数名称前面加上var关键字就可以定义一个变量参数:
func alignRight(var string: String, count: Int, pad: Character) -> String {
let amountToPad = count - count(string)
if amountToPad < {
return string
}
let padString = String(pad)
for _ in ...amountToPad {
string = padString + string
}
return string
}
let originalString = "hello"
let paddedString = alignRight(originalString, , "-")
// paddedString is equal to "-----hello"
// originalString is still equal to "hello”
注意:对变量参数的修改在函数调用结束之后就不存在了,并且变量参数在函数外部是不可见的,它只存在于函数调用的生命周期内。
输入输出参数(In-Out Parameters)
如前所述,变量参数是可以也只能在函数内部被改变,如果你希望函数改变一个参数,并且函数调用结束后仍然有效,就需要将那个参数定义为输入输出参数。通过在参数定义之前加上inout关键字,可以创建一个in-out参数。一个in-out参数有一个被传入函数的值,这个值在函数内部被改变,然后被传回到函数之外替换这个参数的初始值。只能用变量创建输入输出参数,常量和字面量都不可以。在调用函数时,在变量名称之前加上(&)号标明这个参数可以被函数修改:
func swapTwoInts(inout a: Int, inout b: Int) {
let temporaryA = a
a = b
b = temporaryA
} var someInt =
var anotherInt =
swapTwoInts(&someInt, &anotherInt)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3”
注意:in-out参数不能有默认值,可变参数(variadic parameters)也不能被标记为inout,如果标记了一个参数为inout,那么不能同时把它标记为var或者let了。
in-out参数并不同于函数的返回值,如上面的例子,函数并没有返回任何值,它是函数能够在函数体作用域外产生影响的一种有效方法。
函数类型(Function Types)
每个函数都有一个特定的函数类型,它是由函数的参数类型和返回值类型组成的。比如:
func addTwoInts(a: Int, b: Int) -> Int {
return a + b
}
func multiplyTwoInts(a: Int, b: Int) -> Int {
return a * b
}
这两个简单的函数都接受两个Int类型参数,然后返回一个Int类型值,因此它们的函数类型都是 (Int, Int) -> Int 。这个读作“一个包含两个Int型参数并且返回一个Int类型值的函数类型”。如果函数没有参数,也不返回值,则函数类型就是 () -> () 。后边这对空括号表示函数返回Void,它在Swift中被表示为空的元组。
函数类型可以像Swift中的所有其他类型一样被使用,比如你可以定义一个某函数类型的变量或者常量,并且将某个合适的函数赋值给它:
var mathFunction: (Int, Int) -> Int = addTwoInts
然后就可以像调用普通函数一样把这个变量当做函数调用了。
只要函数类型是一样的,另一个不同的函数也可以被赋值给同一个变量。
作为参数类型的函数类型
函数类型同样也可以作为其他函数的参数类型,这就可以将函数的一部分逻辑实现交给函数的调用者去实现,然后作为参数传进函数。比如:
func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
println("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, , )
// prints "Result: 8”
这个例子中,mathFunction的实现是交给函数调用者实现的,比如传入的addTwoInts。
作为返回类型的函数类型
函数类型也可以作为返回类型被其他函数返回。比如:
func stepForward(input: Int) -> Int {
return input +
}
func stepBackward(input: Int) -> Int {
return input -
} func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
return backwards ? stepBackward : stepForward
}
注意这个chooseStepFunction函数的返回类型,它是一个函数类型((Int) -> Int),因此这个函数最终返回的其实就是一个函数。
嵌套函数
之前介绍的函数都是在全局作用域下定义的,因此都是全局函数。函数也可以在函数内部被定义,此时就是嵌套函数了,它的作用域在函数内部,对外默认是隐藏的。但是,当外层函数把嵌套函数当做返回值返回以后,外部环境就可以其他作用域调用这个内部嵌套函数了(闭包的概念)。比如,上面的例子,就可以用嵌套函数的形式实现:
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + }
func stepBackward(input: Int) -> Int { return input - }
return backwards ? stepBackward : stepForward
}
var currentValue = -
let moveNearerToZero = chooseStepFunction(currentValue > )
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != {
println("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
println("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!
Swift学习笔记八的更多相关文章
- Swift学习笔记八:枚举
1. 枚举语法 1)枚举的定义 使用enum关键词而且把它们的整个定义放在一对大括号内: enum SomeEumeration { // enumeration definition go ...
- 【swift学习笔记】二.页面转跳数据回传
上一篇我们介绍了页面转跳:[swift学习笔记]一.页面转跳的条件判断和传值 这一篇说一下如何把数据回传回父页面,如下图所示,这个例子很简单,只是把传过去的数据加上了"回传"两个字 ...
- Swift学习笔记(一)搭配环境以及代码运行成功
原文:Swift学习笔记(一)搭配环境以及代码运行成功 1.Swift是啥? 百度去!度娘告诉你它是苹果最新推出的编程语言,比c,c++,objc要高效简单.能够开发ios,mac相关的app哦!是苹 ...
- Learning ROS forRobotics Programming Second Edition学习笔记(八)indigo rviz gazebo
中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 Learning ROS forRobotics Pro ...
- python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑
python3.4学习笔记(八) Python第三方库安装与使用,包管理工具解惑 许多人在安装Python第三方库的时候, 经常会为一个问题困扰:到底应该下载什么格式的文件?当我们点开下载页时, 一般 ...
- Go语言学习笔记八: 数组
Go语言学习笔记八: 数组 数组地球人都知道.所以只说说Go语言的特殊(奇葩)写法. 我一直在想一个人参与了两种语言的设计,但是最后两种语言的语法差异这么大.这是自己否定自己么,为什么不与之前统一一下 ...
- 【opencv学习笔记八】创建TrackBar轨迹条
createTrackbar这个函数我们以后会经常用到,它创建一个可以调整数值的轨迹条,并将轨迹条附加到指定的窗口上,使用起来很方便.首先大家要记住,它往往会和一个回调函数配合起来使用.先看下他的函数 ...
- go微服务框架kratos学习笔记八 (kratos的依赖注入)
目录 go微服务框架kratos学习笔记八(kratos的依赖注入) 什么是依赖注入 google wire kratos中的wire Providers injector(注入器) Binding ...
- Redis学习笔记八:集群模式
作者:Grey 原文地址:Redis学习笔记八:集群模式 前面提到的Redis学习笔记七:主从复制和哨兵只能解决Redis的单点压力大和单点故障问题,接下来要讲的Redis Cluster模式,主要是 ...
随机推荐
- linq数据使用
取出数据库满足条件的记录的ID,把值放到list中 ) { int userid = Convert.ToInt32(Request.Cookies["id"].Value); v ...
- 组建你自己的Theme,组件你的Style
Andorid-Style,组建你自己的Theme,组件你的Style 前言: 今天,尝试了一个新的Demo,也尝试深入学习,话不多说,看一下,这个Demo如何实现的自定义主题与组件Style是如何绑 ...
- [WebService]之DTD
文档类型定义(DTD)可定义合法的XML文档构建模块.它使用一系列合法的元素来定义文档的结构. DTD 可被成行地声明于 XML 文档中,也可作为一个外部引用. <?xml version=& ...
- Linux配置静态IP
在一块SSD的CentOS配置静态IP 1. 配置静态IP #vi /etc/sysconfig/network-scripts/ifcfg-eth0 DEVICE="eth0" ...
- 省时的浏览器同步测试工具 browsersync NodeJS
http://www.browsersync.cn/ 省时的浏览器同步测试工具 Browsersync能让浏览器实时.快速响应您的文件更改(html.js.css.sass.less等)并自动刷新页面 ...
- jszs 快速排序
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- dom cookie记录用户名
<!doctype html> <html> <head> <meta charset="utf-8"> <title> ...
- 第三百二十三天 how can I 坚持
人在最绝望的时候会干啥,<进击的巨人>. 可以绝望,但一定要相信还有希望. 今天去看了<美人鱼>,确实挺好吧. 把愤怒归结于无能,其实是大错特错,愤怒是人的情绪的发泄,是人就有 ...
- C++builder XE 安装控件 及输出路径
C++builder XE 安装控件 与cb6不一样了,和delphi可以共用一个包. 启动RAD Studio.打开包文件. Project>Options>Delphi Compile ...
- UVALive 3959 Rectangular Polygons (排序贪心)
Rectangular Polygons 题目链接: http://acm.hust.edu.cn/vjudge/contest/129733#problem/G Description In thi ...