Swift教程之函數詳解。本站提示廣大學習愛好者:(Swift教程之函數詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是Swift教程之函數詳解正文
函數是履行特定義務的代碼自包括塊。給定一個函數稱號標識, 當履行其義務時便可以用這個標識來停止”挪用”。
Swift的同一的功效語法足夠靈巧來表達任何器械,不管是乃至沒有參數稱號的簡略的C作風的函數表達式,照樣須要為每一個當地參數和內部參數設置龐雜稱號的Objective-C說話作風的函數。參數供給默許值,以簡化函數挪用,並經由過程設置在輸出輸入參數,在函數履行完成時修正傳遞的變量。
Swift中的每一個函數都有一個類型,包含函數的參數類型和前往類型。您可以便利的應用此類型像任何其他類型一樣,這使得它很輕易將函數作為參數傳遞給其他函數,乃至從函數中前往函數類型。函數也能夠寫在其他函數中來封裝一個嵌套函數用以規模內有效的功效。
1、函數的聲明與挪用
當你界說一個函數時,你可認為其界說一個或多個分歧稱號、類型值作為函數的輸出(稱為參數),當該函數完成時將傳回輸入界說的類型(稱為作為它的前往類型)。
每個函數都有一個函數名,用來描寫了函數履行的義務。要應用一個函數的功效時,你經由過程應用它的稱號停止“挪用”,並經由過程它的輸出值(稱為參數)來婚配函數的參數類型。一個函數的供給的參數必需一直以雷同的次序來作為函數參數列表。
例如鄙人面的例子中被挪用的函數greetingForPerson,像它描寫的那樣 — 它須要一小我的名字作為輸出並前往一句問候給誰人人。
func sayHello(personName: String) -> String {
let greeting = "Hello, " + personName + "!"
return greeting
}
一切這些信息都匯總到函數的界說中,並以func症結字為前綴。您指定的函數的前往類型是以箭頭->(一個連字符後跟一個右尖括號)和隨後類型的稱號作為前往的。
該界說描寫了函數的感化是甚麼,它希冀吸收甚麼,和當它完成前往的成果是甚麼。該界說很輕易讓該函數可讓你在代碼的其他處所以清楚、明白的方法來挪用:
println(sayHello("Anna"))
// prints "Hello, Anna!"
println(sayHello("Brian"))
// prints "Hello, Brian!"
經由過程括號內String類型參數值挪用sayHello的函數,如的sayHello(”Anna”)。因為該函數前往一個字符串值,sayHello的可以被包裹在一個println函數挪用中來打印字符串,看看它的前往值,如上圖所示。
在sayHello的函數體開端界說了一個新的名為greeting的String常量,並將其設置加上personName小我姓名構成一句簡略的問候新聞。然後這個問候函數以症結字return來傳回。只需問候函數被挪用時,函數履行終了是就會前往問候語確當前值。
你可以經由過程分歧的輸出值屢次挪用sayHello的函數。下面的例子顯示了假如它以”Anna”為輸出值,以”Brian”為輸出值會產生甚麼。函數的前往在每種情形下都是量身定制的問候。
為了簡化這個函數的主體,聯合新聞創立和return語句用一行來表現:
func sayHello(personName: String) -> String {
return "Hello again, " + personName + "!"
}
println(sayHello("Anna"))
// prints "Hello again, Anna!"
2、函數的參數和前往值
在swift中函數的參數和前往值長短常具有靈巧性的。你可以界說任何器械不管是一個簡略的僅唯一一個未定名的參數的函數照樣那種具有豐碩的參數稱號和分歧的參數選項的龐雜函數。
多輸出參數
函數可以有多個輸出參數,把他們寫到函數的括號內,並用逗號加以分隔。上面這個函數設置了一個開端和停止索引的一個半開區間,用來盤算在規模內有若干元素包括:
func halfOpenRangeLength(start: Int, end: Int) -> Int {
return end - start
}
println(halfOpenRangeLength(1, 10))
// prints "9"
無參函數
函數並沒有請求必定要界說的輸出參數。上面就一個沒有輸出參數的函數,任什麼時候候挪用時它老是前往雷同的字符串新聞:
func sayHelloWorld() -> String {
return "hello, world"
}
println(sayHelloWorld())
// prints "hello, world"
該函數的界說在函數的稱號後還須要括號,即便它不帶任何參數。當函數被挪用時函數稱號也要隨著一對空括號。
沒有前往值的函數
函數也不須要界說一個前往類型。這裡有一個版本的sayHello的函數,稱為waveGoodbye,它會輸入本身的字符串值而不是函數前往:
func sayGoodbye(personName: String) {
println("Goodbye, \(personName)!")
}
sayGoodbye("Dave")
// prints "Goodbye, Dave!"
由於它其實不須要前往一個值,該函數的界說不包含前往箭頭( – >)和前往類型。
提醒
嚴厲地說,sayGoodbye功效確切還前往一個值,即便沒有前往值界說。函數沒有界說前往類型但返
回了一個void前往類型的特別值。它是一個的確是空的元組,現實上零個元素的元組,可以寫為()。
當一個函數挪用時它的前往值可以疏忽不計:
func printAndCount(stringToPrint: String) -> Int {
println(stringToPrint)
return countElements(stringToPrint)
}
func printWithoutCounting(stringToPrint: String) {
printAndCount(stringToPrint)
}
printAndCount("hello, world")
// prints "hello, world" and returns a value of 12
printWithoutCounting("hello, world")
// prints "hello, world" but does not return a value
第一個函數printAndCount,打印了一個字符串,然後並以Int類型前往它的字符數。第二個函數printWithoutCounting,挪用的第一個函數,但疏忽它的前往值。當第二函數被挪用時,字符串新聞由第一函數打印了回來,去沒有應用其前往值。
提醒
前往值可以疏忽不計,但對一個函數來講,它的前往值即使不應用照樣必定會前往的。在函數體底部
前往時與界說的前往類型的函數不克不及相容時,假如試圖如許做將招致一個編譯時毛病。
多前往值函數
你可使用一個元組類型作為函數的前往類型前往一個有多個值構成的一個復協作為前往值。
上面的例子界說了一個名為count函數,用它計來算字符串中基於尺度的美式英語中設定應用的元音、子音和字符的數目:
func count(string: String) -> (vowels: Int, consonants: Int, others: Int) {
var vowels = 0, consonants = 0, others = 0
for character in string {
switch String(character).lowercaseString {
case "a", "e", "i", "o", "u":
++vowels
case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m",
"n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z":
++consonants
default:
++others
}
}
return (vowels, consonants, others)
}
您可使用此計數函數來對隨意率性字符串停止字符計數,並檢索統計總數的元組三個指定Int值:
let total = count("some arbitrary string!")
println("\(total.vowels) vowels and \(total.consonants) consonants")
// prints "6 vowels and 13 consonants"
須要留意的是在這一點上元組的成員不須要被定名在該該函數前往的元組中,由於他們的名字曾經被指定為函數的前往類型的一部門。
3、函數參數名
一切下面的函數都為參數界說了參數稱號:
func someFunction(parameterName: Int) {
// function body goes here, and can use parameterName
// to refer to the argument value for that parameter
}
但是,這些參數名的僅能在函數自己的主體內應用,在挪用函數時,不克不及應用。這些類型的參數稱號被稱為當地的參數,由於它們只實用於函數體中應用。
內部參數名
有時當你挪用一個函數將每一個參數停止定名長短常有效的,以注解你傳遞給函數的每一個參數的目標。
假如你願望用戶函數挪用你的函數時供給參數稱號,除設置當地地的參數稱號,也要為每一個參數界說內部參數稱號。你寫一個內部參數稱號在它所支撐的當地參數稱號之前,之間用一個空格來分隔:
func someFunction(externalParameterName localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}
留意
假如您為參數供給一個內部參數稱號,挪用該函數時內部稱號必需一直被應用。
作為一個例子,斟酌上面的函數,它經由過程拔出他們之間的第三個”joiner”字符串來銜接兩個字符串:
func join(s1: String, s2: String, joiner: String) -> String {
return s1 + joiner + s2
}
當你挪用這個函數,你傳遞給函數的三個字符串的目標就不是很清晰了:
join("hello", "world", ", ")
// returns "hello, world"
為了使這些字符串值的目標更加清楚,為每一個join函數參數界說內部參數稱號:
func join(string s1: String, toString s2: String, withJoiner joiner: String)
-> String {
return s1 + joiner + s2
}
在這個版本的join函數中,第一個參數有一個內部稱號string和一個當地稱號s1;第二個參數有一個內部稱號toString和一個當地稱號s2;第三個參數有一個內部稱號withJoiner和一個當地稱號joiner。
如今,您可使用這些內部參數稱號挪用清晰明白的挪用該函數:
join(string: "hello", toString: "world", withJoiner: ", ")
// returns "hello, world"
應用內部參數稱號使join函數的第二個版本功效更富有表示力,用戶習氣應用sentence-like的方法,同時還供給了一個可讀的、意圖明白的函數體。
留意
斟酌到應用內部參數稱號的初志就是為了在他人第一次浏覽你的代碼時其實不曉得你函數參數的目標是甚麼。
但當函數挪用時假如每一個參數的目標是明白的和絕不暧昧的,你其實不須要指定內部參數稱號。
內部參數稱號速記
假如你想為一個函數參數供給一個內部參數名,但是當地參數名曾經應用了一個適合的稱號了,你不須要為該參數寫雷同的兩次稱號。取而代之的是,寫一次名字,並用一個hash符號(#)作為稱號的前綴。這告知Swift應用該稱號同時作為當地參數稱號和內部參數稱號。
這個例子界說了一個名為containsCharacter的函數,界說了兩個參數的內部參數稱號並經由過程放置一個散列標記在他們當地參數稱號之前:
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}
這個函數選擇的參數稱號清楚的、函數體極具可讀性,使的該函數被挪用時沒有歧義:
let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")
// containsAVee equals true, because "aardvark" contains a "v"
參數的默許值
可認為任何參數設定默許值來作為函數的界說的一部門。假如默許值曾經界說,挪用函數時便可以省略該參數的傳值。
留意
將應用默許值的參數放在函數的參數列表的末尾。這確保了一切挪用函數的非默許參數應用雷同的順
序,並明白地表現在每種情形下雷同的函數挪用。
這裡有一個版本,是晚期的join函數,並為參數joiner設置了默許值:
func join(string s1: String, toString s2: String,
withJoiner joiner: String = " ") -> String {
return s1 + joiner + s2
}
假如在join函數被挪用時供給給joiner一個字符串值,該字符串是用來銜接兩個字符串,就跟之前一樣:
join(string: "hello", toString: "world", withJoiner: "-")
// returns "hello-world"
然則,假如當函數被挪用時供給了joiner的沒有值,就會應用單個空格(” “)的默許值:
join(string: "hello", toString: "world")
// returns "hello world"
有默許值的內部稱號參數
在年夜多半情形下,為一切參數供給一個內部帶有默許值的參數的稱號長短常有效的(是以請求)。這將確假如當函數被挪用時供給的值時參數必需具有明白的目標。
為了使這個進程更輕易,當你本身沒有供給內部稱號時,Swift主動為一切參數界說了缺省的參數內部稱號。主動內部稱號與當地稱號雷同,就似乎你在你的代碼中的當地稱號之前寫了一個hash符號。
這裡有一個晚期join函數版本,它不為任何參數供給的內部稱號,但依然供給了joiner參數的默許值:
func join(s1: String, s2: String, joiner: String = " ") -> String {
return s1 + joiner + s2
}
在這類情形下,Swift主動為一個具有默許值的參數供給了內部參數稱號。挪用函數時,為使得參數的目標明白、絕不暧昧,是以必需供給內部稱號:
join("hello", "world", joiner: "-")
// returns "hello-world"
留意
你可以經由過程編寫一個下劃線(_)有選擇停止這類行動,而不是一個明白的界說內部參數稱號。然
而,在恰當情形下有默許值的內部稱號參數老是優先被應用。
可變參數
一個可變參數的參數接收零個或多個指定類型的值。當函數被挪用時,您可使用一個可變參數的參數來指定該參數可以傳遞分歧數目的輸出值。寫可變參數的參數時,須要參數的類型稱號後加上點字符(…)。
傳遞一個可變參數的參數的值時,函數體中是以供給恰當類型的數組的情勢存在。例如,一個可變參數的稱號為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)
// returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8, 19)
// returns 10.0, which is the arithmetic mean of these three numbers
留意
函數可以最多有一個可變參數的參數,並且它必需湧現在參數列表的最初以免多參數函
數挪用時湧現歧義。
假如函數有一個或多個參數應用默許值,而且還具有可變參數,將可變參數放在列表的
最末尾的一切默許值的參數以後。
常量參數和變量參數
函數參數的默許值都是常量。試圖轉變一個函數參數的值會讓這個函數體外部發生一個編譯時毛病。這意味著您不克不及毛病地轉變參數的值。
然則,有時函數有一個參數的值的變量正本長短常有效的。您可以經由過程指定一個或多個參數作為變量參數,而不是防止在函數外部為本身界說一個新的變量。變量參數可所以變量而不是常量,並給函數中新修正的參數的值的供給一個正本。
在參數稱號前用症結字var界說變量參數:
func alignRight(var string: String, count: Int, pad: Character) -> String {
let amountToPad = count - countElements(string)
for _ in 1...amountToPad {
string = pad + string
}
return string
}
let originalString = "hello"
let paddedString = alignRight(originalString, 10, "-")
// paddedString is equal to "-----hello"
// originalString is still equal to "hello"
這個例子界說了一個新函數叫做alignRight,它瞄准一個輸出字符串,以一個較長的輸入字符串。在左邊的空間中填充劃定的字符。在該示例中,字符串”hello”被轉換為字符串”—–hello”。
該alignRight函數把輸出參數的字符串界說成了一個變量參數。這意味著字符串如今可以作為一個部分變量,用傳入的字符串值初始化,而且可以在函數體中停止響應操作。
函數起首找出有若干字符須要被添加到右邊讓字符串以右對齊在全部字符串中。這個值存儲在當地常量amountToPad中。該函數然後將填充字符的amountToPad個字符拷貝到現有的字符串的右邊,並前往成果。全部進程應用字符串變量參數停止字符串操作。
留意
一個變量參數的變更沒有超越了每一個挪用函數,所以對內部函數體是弗成見的。變量參數只能存在於函數挪用
的性命周期裡。
輸出-輸入參數
可變參數,如上所述,只能在函數自己內轉變。假如你想有一個函數來修正參數的值,而且想讓這些變更要保持在函數挪用停止後,你便可以界說輸出-輸入參數來取代。
經由過程在其參數界說的開端添加inout症結字寫用來標明輸出-輸入參數。一個在輸出-輸入參數都有一個傳遞給函數的值,由函數修正後,並從函數前往來調換本來的值。
4、函數類型
每一個函數都有一個特定的類型,包含參數類型和前往值類型,好比:
func addTwoInts(a: Int, b: Int) -> Int {
return a + b
}
func multiplyTwoInts(a: Int, b: Int) -> Int {
return a * b
}
這個例子界說了兩個簡略的數學函數addTwoInts和multiplyTwoInts。每一個函數接收兩個int參數,前往一個int值,履行響應的數學運算然後前往成果
這兩個函數的類型是(Int, Int)->Int可以說明為:
這個函數類型它有兩個int型的參數,並前往一個int類型的值
上面這個例子是一個不帶任何參數和前往值的函數:
func printHelloWorld() {
println("hello, world")
}
這個函數的類型是()->(),或許函數沒有參數,前往void。函數沒有顯式地指定前往類型,默許為void,在Swift中相當於一個空元組,記為()。
應用函數類型
在swift中你可以像任何其他類型一樣的應用函數類型。例如,你可以界說一個常量或變量為一個函數類型,並指定恰當的函數給該變量:
var mathFunction: (Int, Int) -> Int = addTwoInts
可以解讀為:
“界說一個名為mathFunction變量,該變量的類型為'一個函數,它接收兩個int值,並前往一個int值。'設置這個新的變量來援用名為addTwoInts函數的功效。”
該mathFunction函數具有與addTwoInts函數雷同類型的變量,所以這個賦值能經由過程Swift的類型檢討。
如今你可以挪用指定的函數稱號為mathFunction:
println("Result: \(mathFunction(2, 3))")
// prints "Result: 5"
分歧的函數雷同的婚配類型可以分派給雷同的變量,也異樣的實用於非函數性類型:
mathFunction = multiplyTwoInts
println("Result: \(mathFunction(2, 3))")
// prints "Result: 6"
與其他類型一樣,你可以把它敏捷界說成函數類型當你為常量或變量分派一個函數時:
let anotherMathFunction = addTwoInts
// anotherMathFunction is inferred to be of type (Int, Int) -> Int
函數類型的參數
可使用一個函數類型,如(Int, Int)->Int作為另外一個函數的參數類型。這使你預留了一個函數的某些方面的完成,讓挪用者挪用函數時供給。
上面就以打印下面的數學函數的成果為例:
func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
println("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// prints "Result: 8"
這個例子中界說了一個名為printMathResult函數,它有三個參數。第一個參數名為mathFunction,類型為(Int, Int)->Int。您可以傳入相符前提的任何函數類型作為此函數的第一個參數。第二和第三個參數a、b都是int類型。被用作於供給數學函數的兩個輸出值。
當printMathResult被挪用時,它傳遞addTwoInt函數,和整數值3和5。它應用3和5,挪用addTwoInt函數,並打印函數運轉的成果8。
printMathResult的感化是挪用一個恰當類型的數學函數並打印響應成果。那是甚麼功效的完成其實其實不主要,你只需賜與准確的類型婚配就行。這使printMathResult以挪用者類型平安的方法轉換了函數的功效。
函數類型的前往值
可使用一個函數類型作為另外一個函數的前往類型。前往的函數(->)即你的前往箭頭後,立刻寫一個完全的函數類型就做到這一點。
上面的例子界說了兩個簡略的函數,分離是stepForward和stepBackward。stepForward函數前往輸出值自增1,而stepBackward函數前往輸出值自減1。這兩個函數都有一個雷同的類型 (Int) -> Int:
func stepForward(input: Int) -> Int {
return input + 1
}
func stepBackward(input: Int) -> Int {
return input - 1
}
這裡有一個chooseStepFunction函數,它的前往類型是”函數類型(Int) -> Int”。chooseStepFunction前往一個基於布爾參數的stepBackward或stepForward函數類型:
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
return backwards ? stepBackward : stepForward
}
您如今可使用chooseStepFunction選擇一個函數,能夠是加一函數或另外一個:
var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function
上述例子可以斷定步調的正負決議能否須要挪動使得currentValue變量慢慢接近零。currentValue初始值是3,這意味著以後值>0,則前往true,chooseStepFunction前往stepBackward函數。前往函數的援用存儲在一個稱為moveNearerToZero常量裡。
現在moveNearerToZero履行了准確的功效,便可以用來計數到零:
println("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
println("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
println("zero!")
// 3...
// 2...
// 1...
// zero!
5、嵌套函數
迄今為止一切你在本章中碰到函數都是全局函數,在全局規模內界說。其實你還可以在其他函數中界說函數,被稱為嵌套函數。
嵌套函數默許對外界是隱蔽的,但依然可以挪用和應用其外部的函數。外部函數也能夠前往一個嵌套函數,許可在嵌套函數內的另外一個規模內應用。
你可以重寫下面的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)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
println("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
println("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!