当前位置: 首页 > 工具软件 > canDB.swift > 使用案例 >

6.Swift-函数

邹祺
2023-12-01

一、函数的定义和调用

Swift中通过func关键字定义方法,->后面紧跟函数返回值类型。

func sayHello(personName:String) -> String {

    let greeeting = "Hello," + personName + "!"

    return greeeting

}


sayHello("Anna")

sayHello("Brian")


func sayHelloAgain(personName:String) -> String {

    return "Hello," + personName + "!"

}

sayHello("Anna")


二、函数的形参和返回值

1、多形参函数

func halfOpenRangeLength(start:Int,end:Int) ->Int{

    return end - start

}

halfOpenRangeLength(1, 10)


2、无形参函数

func sayHelloWorld() -> String{

    return "Hello,world."

}

sayHelloWorld()


3、无返回值函数

func sayGoodbye(personName:String){

    println("Goodbye,\(personName)!")

}

sayGoodbye("Dave")

严格地说,sayGoodbye函数确实还返回一个值,即使没有定义返回值。没有定义返回类型的函数返回了一个Void类型的特殊值。这仅是一个空元组,这里边没有元素,可以被写成()。


当调用函数时,函数的返回值可以忽略:

func printAndCount(stringToPrint:String) -> Int{

    println(stringToPrint)

    return countElements(stringToPrint)

}

func printWithoutCounting(stringToPrint:String){

    printAndCount(stringToPrint)

}

printAndCount("Hello,wirld")

printWithoutCounting("Hello,wirld")

返回值可以忽略,但一个定义了返回值的函数则必须有返回值。对于一个定义了返回类型的函数来说,如果没有返回值,那么将不允许控制流离开函数的底部。如果试图这样做将出现一个编译时错误。


4、多返回值的函数

你可以使用一个元组类型作为函数的返回类型,来返回一个由多个值组成的复合返回值。

func minMax(array:[Int]) -> (min:Int,max:Int){

    var currentMin = array[0]

    var currentMax = array[0]

    for value in array[1..<array.count]{

        if value < currentMin{

            currentMin = value

        }else if value > currentMax{

            currentMax = value

        }

    }

    return (currentMin,currentMax)

}


let bounds = minMax([8,-6,2,109,3,17])

println("min is \(bounds.min) and max is \(bounds.max)")

注意:此时元组的成员不需要被命名,元组是从函数中返回的,因为它们的名字已经被指定为函数的返回类型的一部分。


5、函数的返回值是可选的元组类型

如果函数将返回一个可能为没有任何值的元组,你可以定义返回值为可选元组类型。注意(Int,Int)?不同于(Int?,Int?),前者是元组可选,可以是没有任何值,而后者是元组的成员是可选类型。

上面的minMax方法没有对传入的数组是否为空作安全检查,如果传入的数组为空,会导致运行时错误。可以改写为:

func minMaxWithSafeCheck(array:[Int]) -> (min:Int,max:Int)?{

    if array.isEmpty{

        return nil

    }

    var currentMin = array[0]

    var currentMax = array[0]

    for value in array[1..<array.count]{

        if value < currentMin{

            currentMin = value

        }else if value > currentMax{

            currentMax = value

        }

    }

    return (currentMin,currentMax)

}


用可选绑定来检查这个方法是返回了正确的元组还是nil。

if let bounds = minMaxWithSafeCheck([8,-6,2,109,3,17]){

    println("min is \(bounds.min) and max is \(bounds.max)")

}


6、函数形参名

func someFunction(parameterName: Int) {

    // function body goes here, and can use parameterName

    // to refer to the argument value for that parameter

}

像这样的函数,这些参数名的仅能在函数本身的主体内使用,不能在调用函数时使用。这种形参类型名称被称之为本地形参名(local parameter name),因为它们只能在函数的主体中使用。


a、外部形参名

有时当你调用一个函数将每个形参进行命名是非常有用的,以表明你把每个实参传递给函数的目的。

 

如果你希望使用你函数的人在调用函数时提供形参名称,那除了本地形参名外,你还要为每个形参定义一个外部形参名称。你写一个外部形参名称在它所支持的本地形参名称之前,之间用一个空格来分隔:

func someFunction(externalParameterName localParameterName: Int) {

    // function body goes here, and can use localParameterName

    // to refer to the argument value for that parameter

}


注意:如果您为形参提供一个外部形参名称,那么外部形参名必须在调用时使用。


例如下面一个连接字符串的方法,调用的时候就不太清楚函数的用意:

func join(s1:String,s2:String,joiner:String) ->String {

    return s1 + joiner + s2

}

join("hello", "world", ",")


可以通过函数的外部形参名把方法改成下面的这样,这样调用者就比较清楚函数的用意了:

func join(string s1:String,toString s2:String,withJoiner joiner:String) -> String{

    return s1 + joiner + s2

}

join(string: "hello", toString: "world", withJoiner: ",")

在这个版本的join函数中,第一个形参的外部名称string,本地名称s1;第二个形参的外部名称toString,本地名称s2;第三个形参的外部名称是withJoiner,本地名称为joiner。


b、外部形参名速记

如果你想为一个函数提供一个外部形参名,然而本地形参名已经使用了一个合适的名称了,那你就不需要两次书写该形参的名称。相反,你可以写一次名字,并用一个hash符号(#)作为名称的前缀。这就告诉Swift使用名称相同的本地行参名称和外部形参名称。

func containsCharacter(#string:String,#characterToFind:Character) -> Bool{

    for characer in string{

        if characer == characterToFind{

            return true

        }

    }

    return false

}

let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")

//true


c、默认形参值

你可以为任何形参定义默认值以作为函数定义的一部分。如果已经定义了默认值,那么调用函数时就可以省略该形参。


注意:请在函数形参列表的末尾放置带默认值的形参。这将确保所有函数调用都使用顺序相同的无默认值实参,并让在每种情况下清晰地调用相同的函数。

func join1(string s1:String,toString s2:String,withJoiner joiner:String = " ") -> String{

    return s1 + joiner + s2

}


join1(string: "Hello", toString: "world", withJoiner: "-")

join1(string: "Hello", toString: "world")

如果在join函数调用时为joiner提供了字符串值,那么该字符串值可以用来连接两个字符串。如果函数调用时没有为joiner提供值,就会使用单个空格(" ")的默认值。


//多个默认形参

func join111(string s1:String,toString s2:String,withJoiner joiner:String = " ",test:String = "111") -> String{

    return s1 + joiner + s2 + test

}


join111(string: "Hello", toString: "world", withJoiner: "-",test:"112")

join111(string: "Hello", toString: "world",test:"12")//竟然可以跳过中间的形参



d、有默认值的外部形参名

在大多数情况下,为所有形参提供一个带默认值的外部名是非常有用的(因此要求)。如果在调用函数的时候提供了一个值,那么这将确保形参对应的实参有着明确的目的。

 

为了使这个过程更容易,当你自己没有提供外部名称时,Swift将为你定义的任何默认形参提供一个自动外部名。这个自动外部名和本地名一样,就像你已经在本地名前添加了hash符号(#)一样。

func join2(s1:String,s2:String,joiner:String = " ") -> String{

    return s1 + joiner + s2

}

join2("hello", "world", joiner: "-")


 注意:在定义形参时,你可以通过使用下划线(_)来代替显示外部名称。不过在适当的情况下,带有默认值形参的外部名通常是优先推荐的。


e、可变形参

一个可变形参可接受零个或多个指定类型的值。当函数被调用时,你可以使用可变形参来指定--形参可以用来传递任意数量的输入值。可通过在形参的类型名后边插入三个点符号(...)来编写可变形参。

 

传递至可变形参的值在函数主体内是以适当类型的数组存在的。例如,一个可变参数的名称为numbers和类型为Double...在函数体内就作为名为numbers类型为Double[]的常量数组。

func arithmeticMean(numbers:Double...) ->Double{

    var total:Double = 0

    for number in numbers{

        total += number

    }

    return total/Double(numbers.count)

}

arithmeticMean(1,2,3,4,5)

arithmeticMean(3,8.25,18.75)


注意:函数最多可以有一个可变形参,而且它必须出现在参数列表的最后,以避免使用多个形参调用函数引发歧义。如果你的函数有一个或多个带有默认值的形参,并且还有可变形参,请将可变形参放在所有默认形参之后,也就是的列表的最末尾。


f、常量形参和变量形参

函数的形参默认是常量。试图在函数体内改变函数形参的值会引发一个编译时错误。这意味着你不能错误地改变形参的值。

 

但是有时候,函数有一个形参值的变量副本是非常有用的。您可以指定一个或多个形参作为变量形参,从而避免在函数内部为自己定义一个新的变量。变量参数是变量而非常量,并给函数一个可修改的形参值副本。

在参数名称前用关键字var定义变量参数:

func alignRight(var string:String,count:Int,pad:Character) ->String{

    let amountToPad = count - countElements(string)

    if amountToPad < 1{

        return string

    }

    let padString = String(pad)

    for _ in 1...amountToPad{

        string = padString + string

    }

    return string

}

let originalString = "hello"//hello

let paddedString = alignRight(originalString, 10, "-")//-----hello

这个例子定义了一个新函数叫做alignRight,用于将一个输入字符串和更长的输出字符串右边缘对齐。所有左侧的空白使用指定的占位符来填充。在这个例子中,字符串"hello"被转化为字符串"-----hello"。

 

alignRight函数把输入的形参字符串定义成一个变量形参。这意味着字符串现在可以作为一个本地变量,用传入的字符串值初始化,并且可以在函数体中进行相应操作。

 

函数首先要找出有多少字符需要被添加到字符串的左侧,从而在整个字符串中靠右对齐。这个值存储在本地常量amountToPad中。该函数然后将填充字符的amountToPad个字符拷贝到现有的字符串的左边,并返回结果。


 注意:你对变量形参所做的改变不会比调用函数更持久,并且在函数体外是不可见的。变量形参仅存在于函数调用的声明周期中。


g、inout形参

如上描述,变量形参只能在函数本身内改变。如果你想让函数改变形参值,并想要在函数调用结束后保持形参值的改变,那你可以把形参定义为in-out形参。

 

通过在形参定义的开始添加inout关键字来编写in-out形参。In-Out形参有一个传递至函数的值,由函数修改,并从函数返回来替换原来的值。

 

你只能传递一个变量作为inout形参对应的实参。你不能传递一个常量或者字面量作为实参,因为常量和字面量不能被修改。当你把变量作为实参传递给inout形参时,需要在变量前添加 & 符号,以表明它可以被函数修改。


提示:inout参数不能有默认值,可变参数的参数也不能被标记为inout。如果您标记参数为inout,它不能同时被标记为var或let。


func swapTwoInts(inout a:Int,inout b:Int){

    let temporaryA = a

    a = b

    b = temporaryA

}

var someInt = 3

var anotherInt = 107

swapTwoInts(&someInt, &anotherInt)

print("someInt is now \(someInt),and anotherInt is now \(anotherInt)")

传入的参数前需要加上&符号!


 注意:inout形参不同于从函数返回一个值。上边swapTwoInts例子没有定义返回类型或者返回值,但它仍然会修改someInt和anotherInt的值。对函数来说,inout形参是一个影响函数主体范围外的可选方式。


三、函数类型

每一个函数都有特定的函数类型,由函数的形参类型和返回类型组成。例如:

//(Int,Int) -> Int 类型

func addTwoInts(a:Int,b:Int) -> Int{

    return a + b

}

func multiplyTwoInts(a:Int,b:Int) -> Int{

    return a * b

}


//() -> ()类型 或者"没有形参的函数,并返回void。"没有指明返回值的函数通常会返回void,在swift中相当于一个空元组,显示为()。

func printHellWorld(){

    print("Hello,world")

}


1、使用函数类型

在swift中您可以像任何其他类型一样的使用函数类型。

var mathFunction: (Int,Int) -> Int = addTwoInts

println("Reslut:\(mathFunction(2,3))")//5

只要函数类型一致,就可以赋值给同一变量:

mathFunction = multiplyTwoInts

println("Reslut:\(mathFunction(2,3))")//6


可以让Swift自己推断函数类型:

let anotherMathFunction = addTwoInts    //(Int,Int) -> Int 类型


2、函数类型作为函数形参类型

可以使用一个函数类型,如(Int, Int)->Int作为另一个函数的形参类型。这使你预留了一个函数的某些方面的函数实现,让调用者提供的函数时被调用。

func printMathResult(mathFunction:(Int,Int)->Int,a:Int,b:Int){

    println("Result:\(mathFunction(a,b))")

}

printMathResult(addTwoInts, 3, 5)//8

该函数真正实现了什么并不重要--它只关心函数的类型是正确的。这使得printMathResult以一种安全类型的方式把自身的功能转换至函数的调用者。


3、函数类型作为函数的返回类型

你可以将一个函数类型作为另一个函数的返回类型。你可以在返回函数的返回箭头(->) 后立即编写一个完整的函数类型来实现。

func stepForward(input:Int)->Int{

    return input + 1

}

func stepBackward(input:Int)->Int{

    return input - 1

}


func chooseStepFunction(backwards: Bool) -> (Int) -> Int {

    return backwards ? stepBackward:stepForward //此处注意?要与backwards相隔一个空格

}

var currentValue = 3

let moveNearerToZero = chooseStepFunction(currentValue > 0)//moveNearerToZero引用了stepBackward()函数


println("Counting to zero:")

while currentValue != 0{

    println("\(currentValue)...")

    currentValue = moveNearerToZero(currentValue)

}

println("zero!")


四、函数嵌套

迄今为止所有你在本章中遇到函数都是全局函数,在全局作用域中定义。其实你还可以在其他函数体中定义函数,被称为嵌套函数。

 

嵌套函数默认对外界是隐藏的,但仍然可以通过它们包裹的函数调用和使用它。enclosing function也可以返回一个嵌套函数,以便在其他作用域中使用嵌套函数。

 

你可以重写上面的chooseStepFunction例子使用并返回嵌套函数:

func chooseStepFunction(backwards:Bool) -> (Int) -> Int{

    func stepForward(input:Int) -> Int{

        return input + 1

    }

    func stepBackward(input:Int) -> Int{

        return input - 1

    }

    return backwards ? stepBackward : stepForward

}


var currentValue = -4

let moveNearerToZero = chooseStepFunction(currentValue > 0)

while currentValue != 0{

    println("\(currentValue)...")

    currentValue = moveNearerToZero(currentValue)

}

println("zero!")


五、总结

这一章介绍了Swift的函数,它跟C差不多风格,但是跟OC就差不大了。它通过关键字func定义,参数包含在()里面,函数返回值类型跟在->后面。重点是函数的形参名这部分内容的介绍。最神奇的就是函数的形参可以默认值、外部形参名、可变形参和inout形参。在调用函数的时候,可以忽略有默认值的形参,有多个有默认值的形参的时候,调用函数的时候,还可以跳过中间某个有默认值的形参,非常的灵活。现总结下重点:

1、当函数有多个返回值时,可以使用元组。考虑到安全问题,建议使用可选元组,这样很方便拿到对应的返回值。

2、为了使函数的调用者更清楚地知道函数的功能,可以使用外部形参名。方法很简单,只需要在对应的本地形参名前面再加上对应的外部形参名就可以了。最简单的方式是在本地形参名前面加上#,让外部形参名和本地形参名一样。我更喜欢后者,很方便,而且减少代码量。

3、可以为形参设置默认值,奇怪的是Swift可以这样调用同一个函数: join1(string: "Hello", toString: "world", withJoiner: "-")

join1(string: "Hello", toString: "world")后者不加,withJoiner:竟然也可以。注意设置了默认值的形参要放在形参列表的最后。

4、Swift会给有默认值的形参提供外部形参名,跟本地形参名一样。所以,有默认值的形参,不需要再设置外部形参名,也不用加#。

5、函数支持可变形参,且只能有一个可变形参,可变形参相当于一个数组,传入的参数类型必须是一样的。可变形参必须放在函数参数列表的最后。

6、函数的形参默认是常量,要在函数内修改形参,需要在定义函数的时候显示的将形参定义为var类型。

7、如果要在函数内修改的形参对函数外部起作用,需要用inout关键字申明函数参数,inout跟var或let不能共存。调用函数,传入实参给inout变量的时候必须传入变量,不能传入不可改变的常量和字面值。且必须记得在变量前加上&符号。

8、函数都是有类型的,函数是可以嵌套的。


参考:

1、The Swift Programming Language

2、http://www.cocoachina.com/ios/20140611/8773.html

 类似资料: