在Swift
中,一个标准的函数
创建是这样的:
func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")
值得注意的是,在调用函数传递参数的时候,我们不光提供了参数的值,同时也提供了参数的名,这样确实牺牲了一点开发效率,但是提升了不少代码的可读性,总的来说肯定是利大于弊的。不过即使我们提供了参数的名,传递参数的顺序仍然不可更改,在上面的例子中,如果写成greet(day: "Tuesday",person: "Bob")
是不可以的。
当然也有一些情况,不传递参数名其实也不影响可读性,比如最常见的print
方法,显而易见它的第一参数都是要输出打印的东西,如果我们画蛇添足,明明可以print("Hello Word!")
的,却非要写成print(items: "Hello Word!")
就会显得过于鸡肋了。所以Swift
提供一种方式,可以声明这个参数在调用时不需要提供名,就像这样:
func greet(_ person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("Bob", day: "Tuesday")
在声明函数的时候,在其参数名之前通过_
标记一下,即可让这个参数在调用时免去提供参数名的麻烦。并且需要被_
的参数,没有顺序要求,像下面这样的代码也是可以的:
func greet(person: String, _ day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet(person: "Bob","Tuesday")
这里还有一个不怎么常用的小技巧,就是_
的位置也可以替换成其他内容,从而对这个参数起一个别名,比如这样:
func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")
函数的返回值是通过->
标记的,这点在上面的例子上也已经看到了。如果需要返回多个值的话,可以使用Tuple
(元组类型),比如这样:
func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var min = scores[0]
var max = scores[0]
var sum = 0
for score in scores {
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
return (min, max, sum)
}
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
print(statistics.2)
上面的函数接收一组整型数据作为参数,并计算出这组数据的最大值、最小值以及总和,把这些结果通过封装在在元组中返回,以达到返回多值的效果。
在一个函数内部还可以继续定义函数,并且遵循了大家已知的闭包作用域的规范:
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()
Swift
中的函数,是遵循first-class
原则,也是一等公民,跟其他数据类型一样享受同样的待遇,当然也可以作为一个函数的返回值,比如:
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
我们可以通过print(increment.self)
查看increment
变量的类型,得到的结果会是(Function)
。
函数既然能被用作返回值,当然也可以当作参数进行传递,比如:
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(list: numbers, condition: lessThanTen)
像上面这些把一个函数看成传统类型,一会当返回值一会当参数的玩法在其他很多语言里都是有的。对初学者来说,可能理解起来会稍有困难,主要还是因为实践的少,遇到的场景不多。我觉得没关系,你只要留个印象,以后在阅读别人代码的时候,如果遇到了,自己花点时间能读懂就行了。至于融会贯通、学以致用,可以慢慢来,随着经验的积累自然就会好的。
下面我们再来谈谈Closures
(闭包),首先我们准备一个数组,就用前面的那个例子:
var numbers = [20, 19, 7, 12]
如果我想对数组中的每个元素进行一次平方运算,然后获得一个新的数组,首先我需要准备一个能把Int
平方的函数:
func square(number: Int) -> Int{
return number * number
}
而数组提供了一个叫map
的函数,可以让我们方便地对其元素做逐个运算,并把运算结果汇集到新的数组中。所以我们只要对numbers
进行如下的运算就可以了:
numbers.map(square)
这时候我们得到的结果就是:
[400, 361, 49, 144]
但其实我们可以把声明square
和调用map
合在一起写,就像这样:
numbers.map({ (number: Int) -> Int in
return number * number
})
如你所见,声明专门的square
函数或者是用闭包的方式来给map
传递参数都是等价的。而后者的魅力在于可以省略掉一些已知的东西,进而简化成这样:
numbers.map({number in number * number})
这里,我们简化了入参的类型,return
关键字,其实我们还可以更进一步,就连参数number
也可以用内嵌的参数名$0
替代:
numbers.map({$0 * $0})
当然,如果你的闭包是接收多个参数的,就可以在$0
之后继续通过$1
、$2
...获取其他的参数。比如,如果我们要对numbers
排序,就需要提供一个能接收两个参数的闭包,最后写成这样:
numbers.sorted { $0 > $1 }
运算之后的结果为:
[20, 19, 12, 7]
如果你仔细观察sorted
和map
的代码,会发现在sorted
调用闭包的时候,我们省略了闭包外面的()
,这是因为如果函数调用只有一个参数,并且这个参数是个闭包时,为了简化输入,可以省略函数调用的()
。
关于函数和闭包就先介绍到这里了,我们下次见。
阅读相关文章: