Swift4学习笔记6——函数(Function)

Swift的函数和C的函数定义方式有些区别,它是将返回类型写在函数的最后。一般定义函数的语法如下

func函数名 (参数列表) -> 返回值 {
       //函数体
}

举一个例子,这个函数输入一个字符串,然后打印出这个字符串,并且返回一个字符串。

//函数定义
func printYourName (name: String)->String {
    print(name)
    return "Hello, " + name
}

var s = printYourName(name: "Tom")   //函数调用
print(s)    //打印 Hello, Tom

如果一个函数没有返回值,那么从 -> 返回值 这个部分可以省略。比如下面例子

//函数定义
func printYourName (name: String) {
    print(name)
}

再来看看有多个参数的情况,比如下面例子。

func printTwoString(firstString: String, secondString: String) {
    print(firstString,secondString)
}
printTwoString(firstString: "hello", secondString: "Kate")

Function Argument Labels and Parameter Names

这部分相对Swift2有改变。

先了解一个概念,在函数定义时候,参数列表中使用的fristString和secondString称为参数(Parameter).但是参数包含了两个东西:
1是Argument Label,它是在方法调用的时候写在参数值前面的参数标记,比如下面调用中的firstString和secondString。

printTwoString(firstString: "hello", secondString: "Kate") 

2是Parameter Names,它只的是在方法体里面使用到的参数标志。如printTwoString方法体里面print种使用到的firstString,secondString。
默认情况下,Argument Label和Parameter Names是一样的。但是你也可以自定义Argument Label,方法是在Parameter Names前面加上另外一个字符串,并用空格相隔,如下,begin是自定义的Argument Label,然后调用的时候就需要使用begin来指定参数。

func printTwoString(begin firstString: String, secondString: String) {
    print(firstString,secondString)
}
printTwoString(begin: "hello", secondString: "Kate") 

你可能注意到了使用print的时候没有加上任何的Argument Label。如果你不想要Argument Label,那么在定义方法的时候,将Argument Label的字符串写为下划线 _ 。

func printTwoString(firstString: String, _ secondString: String) {
    print(firstString,secondString)
}
printTwoString(firstString: "hello",  "Kate")  //忽略了第二参数的外部参数名之后,这里就不能加上外部参数名了

参数默认值

此外,还可以给参数赋值默认值。具有默认值的参数,在调用的时候,可以不用给它赋值。好比print方法,它的原型是

print(_:separator:terminator:)

但是一般使用的时候都只传了一个字符串,原因就在于它后面的两个参数都是具有默认值的。给参数设置默认值的方法是在方法定义的时候,在参数的后面用 = 加上默认值。如下代码。
官方文档建议我们把带默认值的参数放在参数列表的末尾,这样在调用的时候不至于混淆。但是其实可以对每个参数都赋值默认值。比如下面的例子。

func printTwoString(firstString: String = "hello", secondString: String = "Lucy", thirdString: String = "end") {
    print(firstString,secondString,thirdString)
}
printTwoString( secondString:"two")  //使用外部参数名指定要赋值的参数,其他参数使用默认值,输出 hello two end

值得注意的是,如果没有默认值的参数在调用的时候也没有给其赋值,那么会在编译的时候报错。
如果你又把参数列表的Argument Label都去掉的话,那么在调用的时候,你给的参数将会从头开始匹配。如果参数类型不匹配的话,就会报错。当然,不建议大家这样做,因为会导致程序的可读性变差。

可变参数列表

func printStrings(strings: String...) {
    print(strings)
}

printStrings("1","2","3")  //输出 ["1", "2", "3"]

通过输出我们可以看到,可变参数在函数体内是以数组的类型存在的。这点在官方文档上有说明。

In-Out 参数

在默认的情况下,参数传递给方法后都是常量,也就是说不能在函数体里面对参数进行修改。这个常量是个形参,不是之前的实参。

func add(first: Int, _ second: Int) -> Int{
    first = 2  //这句报错
    return first + second
}

有一种情况,我们希望在方法里面改变实参的值,所以有了inout关键字,这个关键字不能对可变参数添加,同时加上了这个keyword之后,不能再添加 var let,也不能有默认值。
然后调用的时候,这个参数必须传递一个变量,而不能是常量,并且在变量前加&。

func add(first: inout Int, _ second: Int) -> Int{
    first = 2
    return first + second
}
var a = 1
print("result = \(add(first: &a, 3)), a = \(a) " )//输出 result = 5, a = 2 

关于In-Out,这个实现原理是先将实参copy,然后在方法体内处理,方法结束的时候,再把copy覆盖回原来的实参。所以如果你在方法体里面去改变实参(通过某些方法获得),那么在方法结束的时候,你对实参的改变会被形参覆盖。建议不要在方法体里面操作InOut参数的实参。
关于InOut参数的捕获问题,请参见官方文档
In-Out Parameters

函数类型

函数也是一种类型。函数类型由函数定义决定。比如

func add(first: inout Int, _ second: Int) -> Int{
    first = 2;
    return first + second
}

它的函数类型为 (inout Int, Int) -> Int
如果没有参数也没有返回值的函数,函数类型为 () -> void,也可以写为 () -> ()
函数类型可以和基本类型一样,用来定义变量。继续利用上面定义的add函数

var mathFunc : (inout Int, Int) -> Int = add

var f = 1;
var s = 2;
let result = mathFunc(&f,s)  //使用函数类型

函数类型可以用做参数或返回值,利用上面定义的mathFunc变量,可以有

func add(first: inout Int, _ second: Int) -> Int{
    first = 2;
    return first + second
}

var mathFunc : (inout Int, Int) -> Int = add

func doMath(mathFunc: (inout Int, Int) -> Int, first: inout Int, second: Int) {
    print("mathFunc = \(mathFunc(&first,second))")
    print("first = \(first)")
}

var f = 1;
var s = 2;
doMath(mathFunc: mathFunc, first: &f, second: 2)
print("f = \(f)")
//输出
//mathFunc = 4
//first=2
//f=2

嵌套函数

故名思议,就是在函数里面再定义函数。这个嵌套函数可以在函数内部调用,也可以作为返回值返回,使得它可以在其他范围内进行使用。例子如下
//定义了add 和 sub 两个嵌套函数,然后用于返回。如果输入的不是”+“或”-“,那么返回一个nil。注意giveMeFunc返回的是一个函数类型的可选类型

func giveMeFunc(opt: Character) -> ((Int, Int) -> Int)? {
    var method : ((Int, Int) -> Int)?
    switch opt {
    case "+" :
        func add(one: Int, _ two: Int) -> Int { return one + two }
        method = add
    case "-" :
        func sub(one: Int, _ two: Int) -> Int { return one - two }
        method = sub
    default :
        method = nil
    }
    return method
}

if let m = giveMeFunc(opt:"-") {
    print(m(1, 2))  // 打印  -1, 留意一下,这里没有Argument Label。
}

操作符方法(Operator Methods)

swift和C++一样,可以定义操作符函数。操作符指的是+,-,/,%,+=等等。一般我们这些操作符是给数字类型使用的。但是有了操作符函数之后,我们可以自定义这类符号的运算规则。下面是官方的示例:

struct Vector2D {
    var x = 0.0, y = 0.0
}
extension Vector2D {
    static func + (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y + right.y)
    }
}

上面的例子定义了一个操作符函数 + ,这个操作符函数的参数列表里面有两个参数left和right。分别代表着+号左右两边的两个参数。通过这个函数,我们可以直接将两个Vector示例进行相加。如下:

let vector = Vector2D(x: 3.0, y: 1.0)
let anotherVector = Vector2D(x: 2.0, y: 4.0)
let combinedVector = vector + anotherVector
// combinedVector is a Vector2D instance with values of (5.0, 5.0)

除了这种要接受两个参数的操作符之外,还要一些只有一个参数的操作符,比如 -,++,–等等。但是这类操作符有两类:前缀(Prefix)和后缀(Postfix),比如–a,i++;
这类操作符的定义要加上prefix或postfix关键字。语法如下:

extension Vector2D {
    static prefix func - (vector: Vector2D) -> Vector2D {
        return Vector2D(x: -vector.x, y: -vector.y)
    }
}

上面定义了一个前缀的 - 操作符函数。用来将一个向量取反。后置操作符的关键字是postfix,中间操作符的关键字是infix。

另外还有一种计算并赋值的操作符,比如++,+=等等。这类的操作符会对其中的一个操作对象进行操作后的赋值。所以必须将参数设置为inout

extension Vector2D {
    static func += (left: inout Vector2D, right: Vector2D) {
        left = left + right
    }
}

除了Swift已经定义的操作符之外,还可以自己定义操作符。比如下面定义了一个+++操作符。

prefix operator +++ 

上面的只是定义,我们还需要实现这个操作符所做的事情。

extension Vector2D {
    static prefix func +++ (vector: inout Vector2D) -> Vector2D {
        vector += vector
        return vector
    }
}

但是这个自定义的操作符有一些规定。
自定义的操作符可以由/, =, -, +, !, *, %, <, >, &, |, ^, ?,~,和某些Unicode 字符开始,至于是哪些字符可以参考官网。点击网页,在网页最下面
在这些字符之后,可以接上unicode字符。
另外有一些禁止的规定:
1.不能重写一个单单的 ? 号。(可以在字符里面加入?号)
2.不能重写这些操作符 =, ->, //, /, /, ., 但是可以重写两个或更多个点的操作符。
3. 不能以这些字符开头 ?, <, &
4.不能以这些字符结尾 ?, >, !

在定义的操作符的时候末尾的那对大括号是有用的。在数学上,加减乘除是有优先级和结合规则的。同样的,这里的操作符也是。我们可以在定义操作符的大括号里面定义这个操作符的优先级和结合规律。

infix operator +-: AdditionPrecedence
extension Vector2D {
    static func +- (left: Vector2D, right: Vector2D) -> Vector2D {
        return Vector2D(x: left.x + right.x, y: left.y - right.y)
    }
}

上面例子定义的是一个中间的操作符,它的结合规则是向左结合。优先级是AdditionPrecedence组,这个参考下面的Precedence Group Declaration链接。
前置操作符和后置操作符不能指定优先级,它们作用在同一个操作数,那么先执行后置操作符。

参考网页
Swift函数文档
Swift高级操作符
Swift词汇结构
Precedence Group Declaration

你可能感兴趣的:(Swift学习笔记,swift4,swift)