前言:
本篇仅为视频学习笔记
函数类型 (Function Type)
★每一个函数都是有类型的,函数类型有形式参数类型、返回值类型组成
例子 -1
func test() { } // () -> Void 或者() -> ()
我们看上边这个函数,其实它的类型就是() -> Void,其中()是参数列表 Void它是返回值,由于它没有参数,所以写成(),返回值写一个Void。Void等价于(),所以函数的类型也可以写成() -> ()。综上,函数的类型可以写成() -> Void或者() -> ()。
我们再看下一个。
例子 -2
func sum(a: Int, b: Int) -> Int { return a + b } // (Int,Int)-> Int
它的函数类型是什么呢?是 (Int,Int)-> Int。这样一看,就会明白,它呢,接收了两个Int类型的参数,返回值类型为Int类型
我们再看下一个。
例子 -3
// 定义变量 var fn: (Int,Int) -> Int = sum fn(2,3) // 5,调用时不需要参数标签
我们可以定义变量,定义一个fn变量,那么这个变量的类型是一个函数类型,既然你这个变量是(Int,Int) -> Int类型,我们就可以赋值一个函数名给fn。当然你赋值的函数名必须符合这个类型。假如呢,你把例子1中的test赋值给fn的话,那肯定会报错。因为sum刚好是这种类型的,所以sum这个函数名可以赋值给fn。那么赋值之后呢,我们就可以利用这个变量去调用这个函数。一调用,就把2和3传递给例2中的a和b了。到时候,就会执行函数sum,内部函数体代码。
像利用变量去调用一个函数的话,那么参数标签是不需要写的。相当于以前,你调用sum将函数的时候,是要写a和b这两个标签的。但是一旦传给一个变量,你就直接写2和3,它会按顺序传递给他们(a和b)。
函数类型作为函数参数
func sum(v1: Int, v2: Int) -> Int { return v1 + v2 } func difference(v1: Int, v2: Int) -> Int { return v1 - v2 } func printResult(_ mathFn:(Int,Int) -> Int, _ a: Int, _ b: Int) { print("Result: \(mathFn(a,b))") } printResult(sum, 5, 2) // Result: 7 printResult(difference, 5, 2) // Result: 3
我们来看一个问题,我们定义了两个函数,一个是做加法的,一个是减法的,然后我们又定义了一个函数printResult来打印结果。函数printResult接收了三个参数,而且这三个参数都写了下划线,那么调用的时候,就不用写标签了。
第一个参数是函数类型,说白了第一个参数要求你传一个(Int,Int) -> Int这种类型的函数,然后第二个、第三个参数传Int。那你思考一下,我们是不是可以这样传呢?
printResult(sum, 5, 2) // Result: 7 printResult(difference, 5, 2) // Result: 3
因为我们这个sum函数跟difference这两个函数是不是就是(Int,Int) -> Int这种类型的。所以呢,可以这样传。
那么,传递进去,我会怎么做呢?我会把你传进来的这个函数调用一下,把a和b传到mathFn(a,b)这个函数里。说白了,如果我传的是这个 printResult(sum, 5, 2) ,那么就是把5和2传到了sum函数里去调用。printResult(difference, 5, 2)这个是将5和2传递到difference函数里去调用。并且把这个结果,利用这个斜线小括号放到字符串中打印出来,所以最后打印出来printResult(sum, 5, 2)结果是Result: 7。 printResult(difference, 5, 2)结果是Result: 3。甚至以后,将函数变成一个属性。
甚至以后,将函数变成一个属性。你看下面代码:
以后我们在写一个类的时候,这个类的属性,也就是大家所认为的成员变量,是不是可以有Int类型,也可以有函数类型。到时候可以将一个函数赋值给它。
函数类型作为函数返回值
当然,既然是类型也可以作为函数的返回值类型。比如说举个例子如下:
func next(_ input: Int) -> Int { return input + 1 } func previous(_ input: Int) -> Int { return input - 1 } func forward(_ forward: Bool) -> (Int) -> Int { return forward ? next : previous } forward(true)(3) // 4 forward(false)(3) // 2
我这个next函数,是你传一个东西给我,我就往前走就是加1。previous函数就是你传一个东西给我,我就往后走减1。这样说吧,next是上一步,previous是下一步。上一步就加1,下一步就减1。
因为函数体,就一个表达式,可以不用写return这个家伙。不写的时候,相当于直接return这个家伙。
forward函数,是你传一个Bool布尔类型给我,我来决定返回哪一个函数给你。你会发现forward函数的返回值是一个函数** (Int) -> Int**。这里千万不要搞晕了。
首先,这一个这一个符号,是 forward函数的返回值的开头,右边那个就是返回值类型,说白了,它返回的是一个函数。然后,这个函数要求接收一个Int类型参数,返回一个Int类型。
很明显 next函数和previous函数都是符合条件的。它们都是接收一个Int,返回一个Int。所以注意看,forward函数中,你传递一个布尔类型Bool给我,我发现这个布尔类型Bool是true,我就返回next函数,否则返回previous这个函数。
所以呢,大家注意看一个问题。到时候,我调用forward这个函数的时候,如果你传一个true进来,我返回的是next函数。所以这个next函数一调用,传一个3的话,就会赋值给next函数中input这个标签,去执行函数体代码。所以3+1 = 4。
传一个false,就会返回previous函数,将3传给previous函数中的input这个标签,去执行函数内部代码,其实就是 3 -1,结果为2。
再说一个概念
★ 返回值是函数类型的函数,叫做高阶函数(Higher-Order Function)
typealias
来看一个关键字,typealias这个关键词是什么意思呢?alias是起别名的意思。type是类型,说白了他是给类型,起别名的。
★ typealias用来给类型起别名
那么怎么起别名呢?
typealias Byte = Int8 typealias Short = Int16 typealias Long = Int64
其实我们Swift里面是没有Byte、Short、Long这三个类型的,但是我们可以自己给它们创造一下。
我们思考一下, Byte不就代表一个字节吗?8位。我们可以来一个Int8,Int8不就是代表一个8位的Int类型吗?这样做就可以了。这样就给Int8起了一个别名,叫做Byte。我觉得这个也很好理解,就像将右边Int8赋值给了左边Byte,说白了Byte就等价于Int8。
Short不就是代表两个字节的Int类型吗?我们可以直接将Int16赋值给它(Short)。Long一般代表8个字节,也就是64位的,所以我们将Int64赋值给它(Long)。那么这样,就相当于,我们创造了三个类型吗?
这个起别名,我们给元祖也是可以的。
typealias Date = (year: Int, momth: Int, day: Int) func test(_ date: Date) { print(date.0) print(date.year) } test((2011,9,10))
我们⚠️注意观察,我现在是给元祖取了一个别名,我们将右边的元祖类型(year: Int, momth: Int, day: Int)赋值给了左边的 Date,所以以后用这个 Date就代表了右边的元祖类型 (year: Int, momth: Int, day: Int)。
所以,你注意看 test 函数是接收一个元祖类型 Date,到时候,你思考一下,到时候是不是可以传这个元祖test((2011,9,10))啊。(2011,9,10)中2011、9、10,很明显就是 (year: Int, momth: Int, day: Int)这三个东西。
我将元祖(2011,9,10)传进test函数去了,赋值给了date。拿到元祖,执行函数体代码,我们用点0就date.0可以,访问最前面的成员。也可以通过这个名称year标签,访问到最前面的成员。
然后,我们还可以怎么样呢?给函数类型起一个别名。
总之,你右边能放什么东西呢?类型就行。你可以将右边的类型赋值给左边。既然是放类型,函数类型也是一种类型。如下:
typealias IntFn = (Int,Int) -> Int func difference(v1: Int, v2: Int) -> Int { return v1 - v2 } let fn: IntFn = difference fn(20,10) // 10 func setFn(_ fn: IntFn) { setFn(difference) } func getFn() -> IntFn { return difference }
所以, typealias IntFn = (Int,Int) -> Int 这样写是什么意思呢?你可以用IntFn代表(Int,Int) -> Int 这种函数类型。
那你思考一下,很明显这个 difference函数,就是符合IntFn这种类型的,也就是说白了difference就是符合IntFn这种类型的。
所以,到时候我可以,定义一个IntFn类型的常量fn,然后来存储difference值。这样调用是成功的 fn(20,10) // 10。
到时候,传参也是一样的,我们看setFn函数,要求你传进来的类型是IntFn,那明显difference是符合这个类型的。所以,可以这样传进去 setFn(difference)。
然后,返回值也是一样的。我们getFn,要求返回IntFn类型,所以我这个difference是符合这个类型的,继而返回difference。
按照Swift标准库的定义,Void就是空元祖()
那么什么意思呢?之前提到过,如果你想表达,一个函数没有返回值的话,有三种做法。
第一种,什么也不写,这个右边
func test() { }
第二种,右边可以写一个 -> Void
func test() -> Void { }
第二种,右边可以写一个 -> () 空元祖
func test() -> () { }
那么,为什么这么肯定呢,原因很简单。因为,如果,你去看一下Void这个定义你就会发现,在Swift标准库中有这么一句:
public typealias Void = ()
所以,Void和()是等价的,当然你也可以自己进去看一下。