• Swift 函数


    前言

    • Swift 中函数是用来完成特定任务的独立代码块。

      • 本节所讲的函数概念是全局的,不隶属于某一个数据结构。
      • 没有 “拥有者” 的函数的作用域是全局的,除非重载,否则不可能定义两个一模一样的函数。
      • 在 Swift 语言中没有主函数。
    • 方法是拥有归属关系的函数。

      • 方法的 “拥有者” 可能是类,也可能是结构体等。
      • 方法的 “拥有者” 为方法提供了命名空间,所以在不同的类或者结构体中可以定义完全相同的方法。

    1、函数的定义

    • 函数定义需要关键字 func,其一般格式为

      func 函数名 (参数名1: 参数类型, 参数名2: 参数类型, ...) -> 函数返回类型 {
          
          函数体 .....
          
          return 返回值
      }
      
    • 参数

      • 在函数定义中,我们使用的参数叫形式参数,在调用时传入的参数是实际参数。
      • 参数名后面用冒号相连的是参数类型,在定义函数时,需要明确参数类型。
      • 你可以指定多个参数,参数间用 , 分割。
    • 返回类型

      • 返回值类型使用组合符号 -> 来指示。
      • 如果不需要返回值可以写成 -> Void(注意 V 要大写),或者直接省略 -> 语句。
      • 没有定义返回类型的函数默认会返回 Void
      • 如果 -> 后面是 Void,则函数体中不需要写 return 语句。
    • 函数调用

      • 函数调用时只要传入的参数与函数定义中的参数类型相同即可。
      • 在 Swift 3.0 之前调用函数时,函数的首个参数名是自动隐藏的。
      • 在 Swift 3.0 及之后调用函数时,函数的所有参数是同等地位的,首个参数不再默认隐藏。

    2、函数类型

    • Swift 中函数本身也是一种类型,函数类型通常由函数的形参类型和返回值类型组成。

      • 例如,函数

        func addString(s1: string, s2: string, s2: string) -> String { }
        
        • 的 函数类型 是

          (String, String, String) -> String
          
      • 如果一个函数没有形参或返回值,那么这个函数的类型是 () -> ()

    • 可以像使用 Swift 语言中其他类型一样使用函数类型。

      • 例如可以定义一个函数常量或函数变量,并像一般数据类型指定初始值一样为他指定一个对应的函数。与其他类型一样,当你给函数赋一个变量或者常量时,你可以让 Swift 语言去推断函数的类型。

        func addString(s1: string, s2: string, s2: string) -> String { }
        
        // 指定函数类型
        var addSome:(String, String, String) -> String = addString
        
        // 推断函数类型
        let addSome = addString
        
      • 你也可以将函数作为参数传递给其他函数,或者将函数类型当作返回类型。

        func plus(a: Int, b: Int) -> Int {
            return a + b
        }
        
        func mult(a: Int, b: Int) -> Int {
            return a * b
        }
        
        func someFunction(func: (Int, Int) -> Int, a: Int, b: Int) {
            let result = paraFunc(a, b)
            print("The result is (result)")
        }
        
        someFunction(paraFunc: plus, a: 3, b: 4)     // The result is 7
        someFunction(paraFunc: mult, a: 3, b: 4)     // The result is 12
        

    3、函数参数

    • 在 Swift 语言中,函数的形参和返回值是非常具有灵活性的,在需要的时候,可以定义一个或者多个甚至选择性的省略。

    3.1 外部形参

    • Swift 语言也能支持 OC 的函数参数标签模式,这种模式被称为外部形参。

      • 如果你为参数制定了外部形参名,那么在调用的时候就必须显式的使用。
      • 如果别人第一次阅读你的代码,使用外部形参名称可以使你要表达的意思更加明确,上下文更加清晰。
      • 在函数调用时,如果你不想显示形参名,可以使用下划线 _ 作为外部形参名。
    • 定义格式

      // 使用时本地形参名(本地形参名1、本地形参名2, ...)会被省略
      func 函数名 (外部形参名1 本地形参名1: 参数类型, 外部形参名2 本地形参名2: 参数类型, ...) -> 函数返回类型 {
                  
          函数体 .....
                  
          return 返回值
      }
      
    • 在写外部形参名时,完全可以只写一次名字,只需要用一个 hash 符号 # 作为参数名称的前缀,从而告诉 Swift 我们使用了名称相同的本地形参名称和外部形参名称。

    • 定义格式

      func 函数名 (#参数名1: 参数类型, #参数名2: 参数类型, ...) -> 函数返回类型 {
                  
          函数体 .....
                  
          return 返回值
      }
      
      • 从 Swift 3.0 起不再支持该种定义方式。

    3.2 默认值形参

    • 在 Swift 语言中可以为任何形参定义默认值以作为函数定义的一部分。

      • 如果已经定义了默认值,那么调用函数时就可以省略该形参。
      • 为了避免遗漏参数或者参数传递的二义性,需在函数形参列表的末尾放置带默认值的形参,不要在非默认值的形参前放置。
      • 在有定义默认值的情况下,当没有指定外部形参名称时,Swift 语言将为你定义的任何默认值形参提供一个自动外部形参名,这个自动外部形参名和本地形参名相同。
    • 定义格式

      func 函数名 (参数名1: 参数类型, 参数名2: 参数类型, 默认值形参名: 参数类型 = 默认值) -> 函数返回类型 {
                  
          函数体 .....
                  
          return 返回值
      }
      

    3.3 可变数量形参

    • 当不能确定调用函数需要传入具体参数的数量时,应使用可变形参。

    • 可变形参是指可接受零个或多个指定类型值的形参,可以用它来传递任意数量的输入参数。

      • 声明可变形参时需在参数类型后面使用 ...
      • 当参数传递进函数体后,参数在函数体内可以通过集合的形式访问。
      • 一个函数最多可以有一个可变参数,而且它必须出现在参数列表的最后。
    • 定义格式

      func 函数名 (参数名1: 参数类型, 参数名2: 参数类型, 可变形参名: 参数类型...) -> 函数返回类型 {
          
          函数体 .....
          
          return 返回值
      }
      
    • 可变数量形参的使用

      func arithmeticmean(numbers: Double...) -> Double {
          var total: Double = 0
          for number in numbers {
              total += number
          }
          return total / Double(numbers.count)
      }
      
      print(arithmeticmean(numbers: 1, 2, 3))              // 2.0
      print(arithmeticmean(numbers: 1, 2, 3, 4, 5))        // 3.0
      

    3.4 可变值形参

    • Swift 语言函数的形参默认是常量,我们不能直接在函数体内部改变形参的值,也就是说函数的形参默认是值类型的。

    • 但是如果需要在函数体内部修改函数参数值,可以使用可变参数,

      • 要定义可变参数可以在参数名前使用 var 关键字。
      • 可变参数可以让你能够修改形参的值,它可以给函数体一个可修改的形参值副本。
      • 但这并不意味着可变形参就是引用类型的。
    • 虽然在 Swift 2.2 中仍旧可以使用这个语法,但是在 Swift 3.0 中这种写法已经被废弃了,如果需要修改参数的值,可以在函数体内创建新的成员变量。

    • 定义格式

      func 函数名 (var 参数名1: 参数类型, var 参数名2: 参数类型, ...) -> 函数返回类型 {
          
          函数体 .....
          
          return 返回值
      }
      

    3.5 引用形参

    • 在实际的编码中,我们往往需要在函数体内部修改形参值,并同时作用到实参本身,从而省去增加返回值数量的步骤。

      • 这时可以把形参定义为 in-out(输入输出参数)类型。
      • Swift 3.0 之前,要定义 in-out 类型的参数,需要在参数名前使用 inout 关键字修饰。
      • Swift 3.0 及之后,要定义 in-out 类型的参数,需要在参数类型前使用 inout 关键字修饰。
      • 当把变量传递给 in-out 形参时,必须在变量前添加 “&” 符号,以表明他被函数内部修改的是它本身的引用。
    • 定义格式

      // Swift 3.0 之前
      func 函数名 (inout 参数名1: 参数类型, 参数名2: 参数类型, ...) {
          
          函数体 .....
      }
      
      // Swift 3.0 及之后
      func 函数名 (参数名1: inout 参数类型, 参数名2: 参数类型, ...) {
          
          函数体 .....
      }
      
    • 使用 in-out 参数的同时有几条规则需要注意

      • 只能输入一个变量作为输入输出参数,不能将常量和字面量传递进函数。
      • 不能同时将参数标记为 varletinout
      • 可变形参的参数不能标记为 inout
      • 函数不能有默认值。
    • 引用形参的使用

      func exampleOfFunction(parameter: inout String) -> String {
          parameter = "Hello (parameter)"
          return parameter
      }
      
      var name = "xiaoming"
      
      print(exampleOfFunction(parameter: &name))      // Hello xiaoming
      print(name)                                     // Hello xiaoming
      

    4、嵌套函数

    • 在一个函数体中定义另外一个函数体就称为嵌套函数。

      • 嵌套的函数默认对外是隐藏的,但仍可以通过包裹他们的函数调用和使用它们。
    • 定义格式

      func 函数名1 (参数名1: 参数类型, 参数名2: 参数类型, ...) -> 函数返回类型1 {
                  
          func 函数名2 (参数名3: 参数类型, 参数名4: 参数类型, ...) -> 函数返回类型2 {
                      
              函数体2 .....
                      
              return 返回值2
          }
                  
          函数体1 .....
                  
          return 返回值1
      }
      

    5、函数重载

    • 在 Swift 中函数重载十分常用。

      • 由于 Swift 是一门强类型语言,可以修改参数列表或者使用不同的返回值实现函数重载。
      • 另外 Swift 中有一些特殊的函数是以操作符的形式定义的,比如 ==,如果想让自己定义的数据结构可以使用某个操作符,那么可以对操作符进行重载。
    • 参数列表重载的使用

      func test(s: Int) -> Int {
          print("Int")
          return 1
      }
      
      // 修改参数名
      func test(a: Int) -> Int {
          print("String")
          return 2
      }
      
      // 修改参数类型
      func test(s: String) -> Int {
          print("String")
          return 3
      }
      
      let intValue    = test(s: 0)         // 1
      let intValue    = test(a: 0)         // 2
      let stringValue = test(s: "")        // 3
      
      • 在调用函数时,编译器会检查函数所处的 “上下文”,确定参数名和参数的类型,调用不同的实现。
    • 返回值重载的使用

      func test(s: String) -> Int {
          print("Int")
          return 3
      }
      
      // 修改返回值类型
      func test(s: String) -> String {
          print("String")
          return s
      }
      
      let intValue   : Int    = test(s: "")       // 3
      let stringValue: String = test(s: "")       // ""
      
      • 在调用函数时,编译器会检查函数所处的 “上下文”,确定返回值的类型,调用不同的实现。

      • 因为函数存在多种 “上下文”,因此类型推断不再适用了。

      • 如果写出下面的代码编译器会提示你明确 unKnow 的类型。

        let unKnow = test(s: "")
        
    • 操作符重载的使用

      struct BoxInt {
          var intValue: Int
      }
      
      func == (lhs: BoxInt, rhs: BoxInt) -> Bool {
          return lhs.intValue == rhs.intValue
      }
      
      let b1 = BoxInt(intValue: 1)
      let b2 = BoxInt(intValue: 2)
      
      print(b1 == b2)                 // false
      

    5、函数风格

    • Swift 3.0 之后,函数风格发生了较大的变化,这主要是受 Swift 3.0 提出的 API 设计原则的影响。

    • 1)缩减函数名

      • 在 Swift 3.0 之前,Swift 的函数风格与 OC 类似,函数名需要承担描述首个参数的职责,在函数调用时,函数中首个参数的参数名默认会被隐藏,只在函数体中使用。

      • 从 Swift 3.0 开始,函数的首个参数名不再默认被隐藏,你可以把函数的方法名缩减,把关键字转移到首个参数的参数名上。

        // Swift 3.0 之前
        arr.appendContentsof([4, 5])
        
        // Swift 3.0 及之后
        arr.append(contentsOf: [4, 5])
        
        • 这样无论是添加单个元素,还是添加整个数组的方法都统一为 append,API 更加整齐。
    • 2)用 “时态” 表示函数 “副作用”

      • 在通常的认知中,函数应该是 “无状态” 的,也就是说,无论调用多少次都能得到相同的结果,但是,有时候函数体内部也会修改函数外部的成员值。

        var mutableArr = [1, 3, 2]
        
        // 在 Swift 3.0 之前,sort 方法叫做 sortInPlace
        mutableArr.sort()
        
        // sorted 是 sort 的副作用版本
        let immutableArr = mutableArr.sorted()
        
        • sort 是动词时态,代表这个方法没有副作用,也就是说,sort 方法只会对方法的调用者起作用,是一种原地排序。
        • sortedsort 方法的副作用版本,sorted 方法不会修改原数组,会把原数组拷贝后排序,并且返回排序好的数组,你可以把返回值理解成一种副作用。
    • 3)关键字可以用作参数名

      • 从 Swift 3.0 开始,大多数 Swift 关键字都可以被用作参数名。

        func index(for element: Int, in collection: [Int]) -> Int? {
            for (num, e) in collection.enumerated() {
                if e == element {
                    return num
                }
            }
            return nil
        }
        
        index(for: 3, in: [1, 2, 2, 2, 3])            // 4
        
        • for-in 关键字原本是用在循环中,在这里被用作函数的外部参数名。
  • 相关阅读:
    Image Processing and Analysis_8_Edge Detection:Finding Edges and Lines in Images by Canny——1983
    2019年C题 视觉情报信息分析
    Image Processing and Analysis_8_Edge Detection:Theory of Edge Detection ——1980
    Computer Vision_2_Active Shape Models:Active Shape Models-Their Training and Application——1995
    Computer Vision_1_Active Appearance Models:Active Appearance Models——2001
    Computer Vision_1_Active Appearance Models :Active Appearance Models——1998
    这群程序员疯了!他们想成为IT界最会带货的男人
    阿里云视频云正式支持AV1编码格式 为视频编码服务降本提效
    Knative Serverless 之道:如何 0 运维、低成本实现应用托管?
    千呼万唤始出来——DataV私有部署功能
  • 原文地址:https://www.cnblogs.com/QianChia/p/8625974.html
Copyright © 2020-2023  润新知