Swift-函数

定义和调用函数

在下面的例子中的函数叫做greet(person :),因为这是它的作用 - 它需要一个人的名字作为输入,并返回一个问候语的人。 为了实现这一点,你定义一个输入参数 - 一个String值,称为person - 和一个String的返回类型,它将包含该人的问候语:

func greet(person: String) -> String {
    let greeting = "Hello, " + person + "!"
    return greeting
}

所有这些信息都汇总到函数的定义中,前缀是func关键字。 通过返回箭头 - >(一个连字符后跟一个直角括号)来指示函数的返回类型,后面跟着要返回的类型的名称。

该定义描述了函数的作用,它期望接收的内容,以及它完成后返回的内容。 该定义使得函数可以从代码中的其他位置轻松调用:

print(greet(person: "Anna"))
// Prints "Hello, Anna!"
print(greet(person: "Brian"))
// Prints "Hello, Brian!"

要使此函数的主体更短,可以将消息创建和return语句合并为一行:

func greetAgain(person: String) -> String {
    return "Hello again, " + person + "!"
}
print(greetAgain(person: "Anna"))
// Prints "Hello again, Anna!"

函数参数和返回值

无参数的函数

func sayHelloWorld() -> String {
    return "hello, world"
}
print(sayHelloWorld())
// Prints "hello, world"

多个参数的函数

func greet(person: String, alreadyGreeted: Bool) -> String {
    if alreadyGreeted {
        return greetAgain(person: person)
    } else {
        return greet(person: person)
    }
}
print(greet(person: "Tim", alreadyGreeted: true))
// Prints "Hello again, Tim!"

无返回值的函数

func greet(person: String) {
    print("Hello, \(person)!")
}
greet(person: "Dave")
// Prints "Hello, Dave!"

因为它不需要返回值,所以函数的定义不包括返回箭头( - >)或返回类型。

注意:
严格地说,这个版本的greet(person :)函数仍然返回一个值,即使没有定义返回值。 没有定义的返回类型的函数返回类型为Void的特殊值。 这只是一个空的元组,它写成()。

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

func printAndCount(string: String) -> Int {
    print(string)
    return string.characters.count
}
func printWithoutCounting(string: String) {
    let _ = printAndCount(string: string)
}
printAndCount(string: "hello, world")
// prints "hello, world" and returns a value of 12
printWithoutCounting(string: "hello, world")
// prints "hello, world" but does not return a value

第一个函数printAndCount(string :)打印一个字符串,然后将其字符计数作为Int返回。 第二个函数printWithoutCounting(string :)调用第一个函数,但忽略它的返回值。 当调用第二个函数时,消息仍由第一个函数打印,但不使用返回的值。

具有多个返回值的函数

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(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
// Prints "min is -6 and max is 109"

注意
元组的成员不需要在从函数返回元组的点处命名,因为它们的名称已经被指定为函数的返回类型的一部分。

可选元组返回类型

func minMax(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)
}

您可以使用可选绑定来检查此版本的minMax(array :)函数是否返回实际的元组值或nil:

if let bounds = minMax(array: [8, -6, 2, 109, 3, 71]) {
    print("min is \(bounds.min) and max is \(bounds.max)")
}
// Prints "min is -6 and max is 109"

函数参数标签和参数名称

每个函数参数都具有参数标签和参数名称。 参数标签在调用函数时使用; 每个参数写在函数调用中,其参数标签在它之前。 参数名称用于函数的实现。 默认情况下,参数使用其参数名称作为其参数标签。

func someFunction(firstParameterName: Int, secondParameterName: Int) {
    // In the function body, firstParameterName and secondParameterName
    // refer to the argument values for the first and second parameters.
}
someFunction(firstParameterName: 1, secondParameterName: 2)

所有参数必须具有唯一的名称。 尽管多个参数可能具有相同的参数标签,但唯一的参数标签有助于使代码更具可读性。

指定参数标签

您在参数名称之前写入参数标签,用空格分隔:

func someFunction(argumentLabel parameterName: Int) {
    // In the function body, parameterName refers to the argument value
    // for that parameter.
}

这里有一个greet(person :)函数的变体,它使用一个人的姓名和家乡并返回问候语:

func greet(person: String, from hometown: String) -> String {
    return "Hello \(person)!  Glad you could visit from \(hometown)."
}
print(greet(person: "Bill", from: "Cupertino"))
// Prints "Hello Bill!  Glad you could visit from Cupertino."

省略参数标签

如果不想要参数的参数标签,请为该参数编写下划线(_),而不是显式参数标签。

func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
    // In the function body, firstParameterName and secondParameterName
    // refer to the argument values for the first and second parameters.
}
someFunction(1, secondParameterName: 2)

注意:
如果参数具有参数标签,则在调用函数时必须标记参数。

默认参数值

您可以通过为该参数的类型之后的参数分配值来为该函数中的任何参数定义默认值。 如果定义了默认值,则可以在调用该函数时忽略该参数。

func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
    // If you omit the second argument when calling this function, then
    // the value of parameterWithDefault is 12 inside the function body.
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4) // parameterWithDefault is 12

将没有默认值的参数放在函数的参数列表的开头,在具有默认值的参数之前。 没有默认值的参数通常对函数的意义更重要。

变量参数

传递给可变参数的值在函数体内作为适当类型的数组使用。 例如,具有数字名称和Double ...类型的可变参数在函数体内作为称为[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.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers

输入 - 输出参数

  • 默认情况下,函数参数是常量。试图从函数体内更改函数参数的值会导致编译时错误。这意味着您不能错误地更改参数的值。如果希望函数修改参数的值,并且希望这些更改在函数调用结束后保留​​,请将该参数定义为in-out参数。

  • 您通过将inout参数放在参数类型之前来编写in-out参数。输入 - 输出参数具有传递到函数的值,由函数修改,并被传回函数以替换原始值。有关输入参数和关联编译器优化行为的详细讨论,请参阅输入参数。

  • 您只能将一个变量作为in-out参数的参数传递。您不能将常量或字面值作为参数传递,因为常量和字面值不能修改。在将变量的名称作为参数传递给in-out参数时,在变量名称前放置一个&符号(&),以指示它可以由函数修改。

注意:
输入 - 输出参数不能具有默认值,并且可变参数不能标记为输入。

下面是一个名为swapTwoInts( :)的函数的示例,它有两个in-out整数参数a和b:

func swapTwoInts(_ a: inout Int, _ b: inout Int) {
    let temporaryA = a
    a = b
    b = temporaryA
}

swapTwoInts( :)函数只是将b的值转换为a,将a的值转换为b。 该函数通过将a的值存储在名为temporaryA的临时常量中,将b的值分配给a,然后将temporaryA分配给b来执行此交换。

您可以调用具有两个Int类型变量的swapTwoInts( :)函数来交换它们的值。 请注意,someInt和anotherInt的名称在传递给swapTwoInts( :)函数时,以&符号作为前缀:

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// Prints "someInt is now 107, and anotherInt is now 3"

上面的示例显示,someInt和anotherInt的原始值由swapTwoInts( :)函数修改,即使它们最初在函数之外定义。

输入 - 输出参数与函数返回值不同。 上面的swapTwoInts示例没有定义返回类型或返回值,但它仍然修改someInt和anotherInt的值。 输入 - 输出参数是函数具有超出函数体范围的效果的替代方法。

函数类型

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

func addTwoInts(_ a: Int, _ b: Int) -> Int {
    return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
    return a * b
}

此示例定义了两个简单的数学函数,称为addTwoInts和multiplyTwoInts。 这些函数各取两个Int值,并返回一个Int值。

你在Swift中使用类似任何其他类型的函数类型。 例如,您可以将常量或变量定义为函数类型,并为该变量分配适当的函数:

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

这可以看作:

  • “定义一个名为mathFunction的变量,它的类型是一个函数,它接受两个Int值,并返回一个Int值。设置这个新变量以引用称为addTwoInts的函数。

  • addTwoInts( :)函数与mathFunction变量具有相同的类型,因此Swift的类型检查器允许此赋值。现在可以调用名为mathFunction的已分配函数:

print("Result: \(mathFunction(2, 3))")
// Prints "Result: 5"

与任何其他类型一样,当您将函数分配给常量或变量时,您可以将其留给Swift来推断函数类型:

let anotherMathFunction = addTwoInts
// anotherMathFunction is inferred to be of type (Int, Int) -> Int

函数作为参数

func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// Prints "Result: 8"

函数作为返回值

func stepForward(_ input: Int) -> Int {
    return input + 1
}
func stepBackward(_ input: Int) -> Int {
    return input - 1
}

这里有一个名为chooseStepFunction(backward :)的函数,它的返回类型是(Int) - > Int。 chooseStepFunction(backward :)函数基于一个名为backward的布尔参数返回stepForward(_ :)函数或stepBackward(_ :)函数:

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    return backward ? stepBackward : stepForward
}

你现在可以使用chooseStepFunction(backward :)来获取一个方向或另一个方向的函数:

var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function
print("Counting to zero:")
// Counting to zero:
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// 3...
// 2...
// 1...
// zero!

嵌套函数

func chooseStepFunction(backward: Bool) -> (Int) -> Int {
    func stepForward(input: Int) -> Int { return input + 1 }
    func stepBackward(input: Int) -> Int { return input - 1 }
    return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
    print("\(currentValue)... ")
    currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 函数是用来完成特定任务的独立的代码块。给一个函数起一个合适的名字,用来标识函数做什么,并且当函数需要执行的时候,这...
    穷人家的孩纸阅读 806评论 2 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,600评论 18 139
  • 86.复合 Cases 共享相同代码块的多个switch 分支 分支可以合并, 写在分支后用逗号分开。如果任何模式...
    无沣阅读 1,347评论 1 5
  • 中国人民大学是一所以人文社会科学为主的综合性研究型全国重点大学,直属于教育部,由教育部与北京市共建,是国家“985...
    道法自然008阅读 915评论 0 0
  • 段总阅读 173评论 0 0