Swift 学习笔记(函数)
由于 Swift 出自于名门 Apple,与 Objective-C 共同服务于 iOS/Mac OS X 平台,因而看得出它更像是脱胎于 Objective-C。现在来学习它的函数相关的知识,Swift 函数定义有些像 Scala,只是用 func 替代了 def, 返回类型的指定用的是
当然函数无参数就是空括号(不能省略空括号),无返回值的话就无需
调用函数时和 Objective-C 是一样的,除第一个参数不需要指定形参名,其他参数必须指定形参名,并且顺序是要和函数定义时完全一样的。我们知道在 Objective-C 中一般约定把第一次参数包含在方法名中,如
Scala 调用函数时指定形参名时,参数顺序是随意的,如
Swift 的函数有返回值的话一定要用
外部参数名和本地参数名
和 Objective-C 类似,Swift 函数的参数也分为外部参数名和本地参数名。从外部调用函数时使用外部参数名,函数内部使用本地参数名来引用传入的值。只写一个参数名的话它即是外部也是本地参数名,第一个参数使用起来还比较特殊。
外部参数名和本地参数名全写
有两个参数时
因为只有一个参数名的情况,外部和本地参数名都共用一个名字;再加之调用函数默认时第一个参数名要省略,所以我们可以只是第一个参数具备外部和本地参数名,其余只指定一个参数名,如此可使得调用函数时每个参数都带名称,像
这种调用方式个人觉得比 sayHello("Bill", anotherPerson: "Ted") 或者 sayHelloTo("Bill", anotherPerson: "Ted") 更好更齐整。
通过
函数返回多个值
Swift 也有 Tuple 类型,所以函数要返回多个值的话,就返回一个 Tuple, 如
Tuple 中元素个数随意,用基于 0 的下标进行访问;也能直接赋值给 Tuple 类型,也就同时获得各个值
Optional 参数和 Optional 返回类型
Swift 的参数或返回值可以支持 Optional 类型,还是老办法--像声明 变量一样,在类型后面加个问号,如
可变个数参数(Variadic Parameters)
和 Objective-C(比如 nslog() 函数) 等众多语言一样 Swift 也支持可变个数参数(VarArgs)
注意描述可变参数的格式是类型后面
回顾一下:在 Objective-C 中要实现一个可变参数函数麻烦些了,要这样做
而 Java 和 Scala 相对简单,参数形式分别是 (String... args) 和 (args: String*)。
函数默认参数
用外部参数名去匹配,第一个参数不写外部参数名。但如果写成下面的形式就有点傻眼了
var 函数参数
默认时函数的参数是一个常量,也就是说默认时把参数前的 let 省略了
看看 var 参数是什么样的状况
我们从输出发现,尽管 a, b 都是 var 变量,但它们经过函数调用后,它们的值或内部状态仍然保持不变,因为参数传入函数时产生了一个深拷贝。而 var 类型的函数参数只被借用来过渡一下。
Swift 对待参数的处理方式对于函数式编程意义重大,真正的 Immutable 了,并且实例是不被共享的。
inout 参数
既然上面的 let 和 var 都无法撼动传入参数的值,Swift 还有一绝招,inout 类型参数,像数据库存储过程一样,它所指定的参数引用或内部状态都可以改变。像是 C/C++ 的传地址
参考: 1. Functions 永久链接 https://yanbin.blog/swift-learning-function/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
-> 而不是 =, 格式如下:1func greet(name: String, day: String) -> String {
2 return "Hello \(name), today is \(day)"
3}
4
5greet("Bob", day: "Tuesday")当然函数无参数就是空括号(不能省略空括号),无返回值的话就无需
-> 指定类型了,比如 func greet() { print("Hello")},从形式上看不出是否有副作用(纯函数)调用函数时和 Objective-C 是一样的,除第一个参数不需要指定形参名,其他参数必须指定形参名,并且顺序是要和函数定义时完全一样的。我们知道在 Objective-C 中一般约定把第一次参数包含在方法名中,如
1- (instancetype)initWithNibName:(NSString *)nibName bundle:(NSBundle *)nibBundleScala 调用函数时指定形参名时,参数顺序是随意的,如
1scala> def greet(name: String, day: String) = {
2 | s"Hello $name, today is $day"
3 | }
4greet: (name: String, day: String)String
5
6scala> greet(day="Tuesay", name="Bob")
7res2: String = Hello Bob, today is TuesaySwift 的函数有返回值的话一定要用
-> 指定类型,并且函数中的 return 也是必不可以少的。而 Scala 的函数则用 = 来连接函数体,并能通过函数的的出口语句自动推断出返回类型,反而不能显示用 return 了; 指定了函数返回类型才能用 return.外部参数名和本地参数名
和 Objective-C 类似,Swift 函数的参数也分为外部参数名和本地参数名。从外部调用函数时使用外部参数名,函数内部使用本地参数名来引用传入的值。只写一个参数名的话它即是外部也是本地参数名,第一个参数使用起来还比较特殊。
外部参数名和本地参数名全写
1func someFunction(externalParameterName localParameterName: Int) {
2 print(localParameterName) //函数内部只能用本地参数名来引用参数
3}
4
5someFunction(externalParameterName: 100) //调用函数时可能使用外部参数名来传入参数有两个参数时
1func sayHello(to person: String, and anotherPerson: String) {
2 print("Hello \(person) and \(anotherPerson)!")
3}
4
5sayHello(to: "Bill", and: "Ted") //这样即使是第一个参数也有了外部参数名因为只有一个参数名的情况,外部和本地参数名都共用一个名字;再加之调用函数默认时第一个参数名要省略,所以我们可以只是第一个参数具备外部和本地参数名,其余只指定一个参数名,如此可使得调用函数时每个参数都带名称,像
1func sayHello(to person: String, anotherPerson: String) {
2 print("Hello \(person) and \(anotherPerson)!")
3}
4
5sayHello(to: "Bill", anotherPerson: "Ted")这种调用方式个人觉得比 sayHello("Bill", anotherPerson: "Ted") 或者 sayHelloTo("Bill", anotherPerson: "Ted") 更好更齐整。
通过
_ 代替外部参数名可以让我们调用函数使用 Java 那样的语法 -- 无需指定外部参数名1func sayHello(to: String, _ anotherPerson: String) {
2 print("Hello \(to) and \(anotherPerson)!")
3}
4
5sayHello("Bill", "Ted")函数返回多个值
Swift 也有 Tuple 类型,所以函数要返回多个值的话,就返回一个 Tuple, 如
1func coordinate() -> (Double, Double) {
2 return (42.023261, -87.801290)
3}
4
5coordinate().0 //42.023261
6
7let (x, y) = coordinate()
8print(xTuple 中元素个数随意,用基于 0 的下标进行访问;也能直接赋值给 Tuple 类型,也就同时获得各个值
Optional 参数和 Optional 返回类型
Swift 的参数或返回值可以支持 Optional 类型,还是老办法--像声明 变量一样,在类型后面加个问号,如
1func coordinate(input: String?) -> (Double, Double)? {
2 input.map{i in print(i)} //输出 Demo
3 return (42.023261, -87.801290)
4}
5
6let input:String? = "Demo" //声明一个 Optional 的 String 类型
7coordinate(input).map{value in print(value.0)} //得到的是一个 Optional 的 (Double, Double) 类型可变个数参数(Variadic Parameters)
和 Objective-C(比如 nslog() 函数) 等众多语言一样 Swift 也支持可变个数参数(VarArgs)
1func count(numbers: Int...) -> Int {
2 return numbers.count
3}
4count()
5count(1,2)注意描述可变参数的格式是类型后面
...,numbers: Int...,实际 numbers 类型也是一个数组。回顾一下:在 Objective-C 中要实现一个可变参数函数麻烦些了,要这样做
1- (NSNumber *) addValues:(int) count, ... {
2 va_list args;
3 va_start(args, count);
4
5 NSNumber *value;
6 double retval;
7
8 for( int i = 0; i < count; i++ ) {
9 value = va_arg(args, NSNumber *);
10 retval += [value doubleValue];
11 }
12
13 va_end(args);
14 return [NSNumber numberWithDouble:retval];
15}而 Java 和 Scala 相对简单,参数形式分别是 (String... args) 和 (args: String*)。
函数默认参数
1func foo(a1: String = "X", a2: String = "Y") -> String { //当然可以个别参数有默认值的
2 return a1 + a2
3}
4
5foo() //XY
6foo("a") //aY
7foo("a", a2: "b") //ab
8foo(a2: "b") //Xb用外部参数名去匹配,第一个参数不写外部参数名。但如果写成下面的形式就有点傻眼了
1func foo(a1: String = "X", _ a2: String) -> String {
2 return a1 + a2
3} //想要应用第一个默认参数,只传第二个参数时该如何? foo("a2") 是错的,又不能写成 foo(a2: "a2")var 函数参数
默认时函数的参数是一个常量,也就是说默认时把参数前的 let 省略了
1func foo(number1: Int) { //它是 func foo(let number1: Int) 的简写
2 number1 = 1 //编译出错,提示 cannot assign to value: 'number1' is a 'let' constant
3}
4
5func bar(numbers: [Int]) { //它也是 func bar(let numbers: [Int]) 的简写形式
6 number1[0] = 10 //编译出误,提示 cannot assign through subscript: 'numbers' is a 'let' constant
7}看看 var 参数是什么样的状况
我们从输出发现,尽管 a, b 都是 var 变量,但它们经过函数调用后,它们的值或内部状态仍然保持不变,因为参数传入函数时产生了一个深拷贝。而 var 类型的函数参数只被借用来过渡一下。Swift 对待参数的处理方式对于函数式编程意义重大,真正的 Immutable 了,并且实例是不被共享的。
inout 参数
既然上面的 let 和 var 都无法撼动传入参数的值,Swift 还有一绝招,inout 类型参数,像数据库存储过程一样,它所指定的参数引用或内部状态都可以改变。像是 C/C++ 的传地址
1func inoutfunc(inout number1: Int, inout _ numbers: [Int]) { //inout 就相当于 C/C++ 的传地址
2 number1 = 10
3 numbers[0] = 20
4}
5
6var a = 1
7var b = [2,3]
8inoutfunc(&a, &b) //a, b 必须定义为 var,并且调用时必须用 & 取其地址
9print(a) //输出10
10print(b) //输出 [20, 3]参考: 1. Functions 永久链接 https://yanbin.blog/swift-learning-function/, 来自 隔叶黄莺 Yanbin's Blog
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。