★ 学习笔记:《iOS高级:Swift入门精讲②》第一节 Swift编程-01➡11 函数类型

前言:

本篇仅为视频学习笔记

函数类型 (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和()是等价的,当然你也可以自己进去看一下。


你可能感兴趣的:(★ 学习笔记:《iOS高级:Swift入门精讲②》第一节 Swift编程-01➡11 函数类型)