一个下午让你掌握Swift基础 ( 7/9 ) 函数

简介

这是一个Swift语言教程,基于最新的iOS 9,Xcode 7.3和Swift 2.2,会为你介绍Swift编程非常基础的内容。从电脑如何工作的全程基本原理到语言结构,你会足够了解这门语言,来处理数据和管理代码的行为。

快速链接


函数

函数(Functions)是很多编程语言的核心。简单地说,一个函数让你定义一个代码块来完成指定的任务。然后,无论何时 app 需要执行这个任务,都可以运行这个函数,而不是到处复制和粘贴同样的代码。

这篇文章里,你会学习到如何写自己的函数,以及亲眼目睹 Swift 如何让他们轻松使用。

函数基础

假设你有一个 app 经常需要打印名字。你可以写一个函数来实现:

func printMyName() {
    print("我的名字是张嘉夫。")
}

上面的代码被称作函数声明(function declaration)。你用 func 关键字来定义一个函数。接着是函数的名字,跟着括号。你会在下个部分学习更多有关这些括号的必要性。

括号之后跟着一个开的花括号,后面是你想在函数里运行的代码,跟着一个关闭的花括号。函数定义好之后,你可以这样使用它:

printMyName()

这输出如下内容:

我的名字是张嘉夫。

如果你怀疑你在之前的文章里已经使用了一个函数,那就对啦!print, 在控制台里输出你给它的文本,事实上就是一个函数。

这很好地引向了下个部分,你会在那里学习如何传递数据给一个函数,以及获得返回的数据。

函数参数

在之前的例子里,函数简单地输出了一条消息。这很棒,但有的时候你希望参数化(parameterize)你的函数,这可以让函数进行不同的操作,依据通过它的参数(parameters)传递进来的数据。

举个例子,看下面的函数:

func printMultipleOfFive(multiplier: Int) {
    print("\(multiplier) * 5 = \(multiplier * 5)")
}
printMultipleOfFive(10)

这里,你能看到一个参数的定义,在函数名后面的括号里。它叫做 multiplier,是 Int 类型的。在所有函数里,括含包含被称作参数列表(parameter list)的东西。

函数会输出给的任意值的5倍。这个例子里,你可以带有 10 的值来调用函数,所以函数如下输出:

10 * 5 = 50

你可以更进一步,让函数更通用。带有两个参数,函数可以输出任意两个值的乘积。你可以这样做:

func printMultipleOf(multiplier: Int, andValue: Int) {
    print("\(multiplier) * \(andValue) = \(multiplier * andValue)")
}
printMultipleOf(4, andValue: 2)

现在函数名字后面的括号里有两个参数了:一个叫做 multiplier 并且另一个叫做 andValue。两个都是 Int 类型。

这次,你调用函数的时候传递了两个值。注意第二个参数之前有一个标签。这个语法是 Swift 的方式,让你写的代码读起来就像一个句子。上面的例子里,你会像这样读最后一行代码:

Print multiple of 4 and value 2

你可以让它阅读起来更清晰,通过给一个参数不同的外置(external)名字。
例如,你可以像这样改变 andValue 参数的外置名字:

func printMultipleOf(multiplier: Int, and andValue: Int) {
    print("\(multiplier) * \(andValue) = \(multiplier * andValue)")
}
printMultipleOf(4, and: 2)

你分配了一个不同的外置名字,通过把它写在参数名前面。
这个例子里,andValue 仍然是参数的名字,但调用函数的标签现在简写为 and 了。你可以这样读新的调用:

Print multiple of 4 and 2

如果你根本不想要任何外置名字,那可以用下划线 _,就像你在前面的文章里见到的:

func printMultipleOf(multiplier: Int, _ andValue: Int) {
    print("\(multiplier) * \(andValue) = \(multiplier * andValue)")
}
printMultipleOf(4, 2)

这个例子里,第二个参数没有外置名字,就像第一个参数。但要明智的使用下划线。这里,你的表达仍然可以理解,但带有很多参数的更复杂的函数会变得费解、笨重,如果它没有外置名字的话。想象一下一个带有5个参数的函数!

你也可以给参数默认值:

func printMultipleOf(multiplier: Int, and andValue: Int = 1) {
    print("\(multiplier) * \(andValue) = \(multiplier * andValue)")
}
printMultipleOf(4)

区别是第二个参数后面的 =1,意味着如果第二个参数没有给值,默认就是1。

因此,代码如下输出:

4 * 1 = 4

如果你预料大部分时间一个参数都是一个值,那有一个默认值会是很有用的,并且它会简化你调用函数的代码。

返回值

你到目前为止看到的所有函数都执行一个简单的任务,其实也就是,输出一些东西。函数也能返回一个值。函数的调用者可以把返回值(return value)赋值给一个变量或常量。

这意味着你可以用一个函数来处理数据。通过参数来带进数据,操作它然后返回它。这是定义一个返回值的函数的方法:

func multiply(number: Int, by byValue: Int) -> Int {
    return number * byValue
}
let result = multiply(4, by: 2)

要声明一个返回值的函数,在一对括号和打开的花括号中间,添加一个 -> 紧跟着返回值的类型。这个例子里,函数返回一个 Int。

函数里面,使用 return 关键词来返回值。这个例子里,你返回了两个参数的乘积。

通过使用元祖也可以返回多个值:

func multiplyAndDivide(number: Int, by byValue: Int) -> (multiply: Int,
    divide: Int) {
        return (number * byValue, number / byValue)
}
let result = multiplyAndDivide(4, by: 2)
let multiply = result.multiply
let divide = result.divide

这个函数返回了两个参数的乘积和商。我们定义了函数返回一个元祖,这个元祖包含两个 Int 值,并且带有合适的成员值名称,实现了这个结果。

通过元祖返回多个值的能力让使用 Swift 变得很快乐。并且它被证明是一个非常有用的特色,你马上就会看到。

高级参数管理

传递给函数的参数默认是常量,意味着他们不能被修改。举例说明,看如下代码:

func incrementAndPrint(value: Int) {
    value += 1
    print(value)
}

这会导致一个错误:

 Left side of mutating operator isn't mutable: 'value' is a 'let' constant(修改操作符左侧没法被修改:'value‘ 是一个 'let' 常量)

参数 value 等同于一个用 let 声明的常量。因此,当函数尝试递增它的时候,编译器抛出了一个错误。

要知道 Swift 在值传递给函数之前拷贝了它,一个行为叫做 pass-by-value

**注意:**Pass-by-value 和做拷贝是目前为止你在本教程中看到的所有类型的标准行为。你会在后面的教程看到另一种方式,把东西传递到函数中,“类”。

通常情况下你想要这个行为。理想情况下,一个函数不会修改它的参数。如果它这么做了,然后你就不能确定参数的值,代码里可能会出现错误的假设,导致了错误的数据。

有时候你的确希望让一个函数直接改变一个参数,一个行为叫做 pass-by-reference。像这样做:

func incrementAndPrintInOut(inout value: Int) {
    value += 1
    print(value)
}

参数名前的 inout 关键字表示这个参数应该使用 pass-by-reference。

现在你需要给函数调用做一个轻微调整:

var value = 5
incrementAndPrintInOut(&value)
print(value)

你在参数前加了个 & 符号,帮助你记住这个参数用的是 pass-by-reference。现在函数可以随心所欲改变值。

这个例子会如下输出:

6
6

函数成功递增了 value,在函数结束后还保留了被修改的数据。这就像参数进入(in)了函数,然后又出来了(out),因此是关键词 inout。

标准库的例子

Swift 标准库包含很多准备好的函数供你使用。

你在读本教程的时候已经看见有一个被使用了:print。它携带一个单独的字符串参数,把它输出到控制台。

有一些数学函数值得记忆因为他们经常在手边使用:

第一个就是 max,带有两个值,返回最大值:

let result = max(10, 20)

这个例子里,result 等于 20。

相似的,min 带有两个值,返回最小值:

let result = min(10, 20)

这里,result 等于 10。

最后,abs 带有一个单独的值然后返回绝对值。例如,-10 的绝对值是 10,10 的绝对值是 10。这是 Swift 函数:

let result = abs(-10)

这次,result 等于 10。

当你继续使用 Swift 的时候,你会遇到标准库里的许多其它函数。你不会记住所有的,但重要的是知道存在有很多预定义的函数。如果你觉得可能有一个是为你要做的事准备的,就搜索它。如果 Apple 已经写了一个被完全测试过的函数,那就没必要再重复劳动了!

迷你练习

  1. 写一个叫 printFullName 函数,带有两个字符串,叫做 firstName 和 lastName。函数应该输出全名,定义为 firstName + " " + lastName。用它来输出你自己的全名。
  2. 改变 printFullName 的声明,让它的第二个参数 lastName 没有外置名字。
  3. 写一个叫做 calculateFullName 的函数,把全名用字符串(string)返回。用它把你自己的名字保存为一个常量。
  4. 改变 calculateFullName 返回一个元祖,包含全名和名字的长度。你可以用下面的语法来获得一个字符串的长度:string.characters.count。用这个函数来确定你自己全名的长度。

函数作为变量

这可能会让人吃惊,但 Swift 里的函数只是简单的另一种数据类型而已。你可以给它们赋值变量和常量,就像你可以对任意其它值的类型一样,比如 Int 或 String。

要知道怎么用,看下面的函数:

func add(a: Int, _ b: Int) -> Int {
    return a + b
}

它带有两个参数,返回他们的和。

你可以把这个函数赋值给一个变量,像这样:

var function: (Int, Int) -> Int = add

这里,变量名是 function 并且它的类型是(Int, Int) -> Int。你把 add 函数赋值给这个变量。

然而类型定义很重要。你用函数定义里同样的方式来定义变量,写参数列表和返回类型。这里 function 变量是一个函数类型,带有两个 Int 参数并且返回一个 Int。

现在你可以用 function 变量,和使用 add 同样的方式,像这样:

let result = function(4, 2)

这个例子里,result 等于 6。

现在看看下面的代码:

func subtract(a: Int, _ b: Int) -> Int {
    return a - b
}

现在,你定义了另一个函数,带有两个 Int 参数并且返回一个 Int。你可以设置之前的 function 变量为你的新 subtract 函数,因为 subtract 的参数列表和返回类型和 function 变量的类型是匹配的。

function = subtract
let result = function(4, 2)

这次,result 等于 2。

“可以把函数赋值给变量”会经常在手边用,是因为它意味着你可以传递函数给其他函数。这是它的实际例子:

func printResult(function: (Int, Int) -> Int, _ a: Int, _ b: Int) {
    let result = function(a, b)
    print(result)
}
printResult(add, 4, 2)

printResult 带有三个参数:

  1. function 是一个函数类型,带有两个 Int 参数并且返回一个 Int,像这样声明:(Int, Int) -> Int。
  2. a 没有外置名字,并且类型是 Int。
  3. b 没有外置名字,并且类型是 Int。
    printResult 调用了传入的函数,传给它两个 Int 参数。
    然后它输出结果到控制台:

6

能够传递函数给其它函数是非常实用的,可以帮助你写可复用代码。你不止可以传递数据来操作,函数作为参数传递让你把代码如何执行变得更灵活。

关键点

  • 使用函数(function)来定义一个任务,想执行多少次都可以,不需要多次写代码。
  • 函数可以带有零或更多参数(parameters),并且可以选择性返回一个值。
  • 函数的第一个参数在函数调用里没有标签(label)。所有随后的参数用他们的名字作为标签。
  • 你可以添加一个外置名字(external name)给一个函数参数,来改变函数调用里使用的标签,或者你可以用一个下划线来表示没有标签。
  • 你可以赋值函数给变量,然后传递他们给其它函数。

接下来去哪儿?

函数是组合小的代码块到大的单元的第一步。你会继续这个主题,在下面的文章里学习集合类型(collection types)和结构体(structures)。
在下一篇文章你会学习闭包(closures),和函数相关但更轻量、在“百忙中”也可以轻松声明。继续之前,看看前面的挑战,因为你在理解闭包之前需要理解函数!

挑战

挑战 A:黄金时段

当我在熟悉一门编程语言的时候,我做的第一件事就是写一个函数来确定一个数字是不是素数。这是你的第一个挑战:
首先,写下面的函数:

func isNumberDivisible(number: Int, by byNumber: Int) -> Bool

你会用它来判断一个数字能否被另一个除尽。当 number 可以被 byNumber 除尽的时候应该返回 true。

提示:你可以使用模除(%)操作符来判断一个数字是不是可以被另一个数字除尽:x % y = 0 的时候 x 可以被 y 除尽。
接下来,写主函数:

func isPrime(number: Int) -> Bool

如果一个数字是素数(prime)应该返回 true,否则是 false。一个数字如果只能被 1 和自己除尽,那它就是素数。你应该循环数字,从 1 到 这个数字,寻找它的除数。如果它有任何不适 1 和它自己的除数,那这个数字就不是素数。你会需要使用你之前写的 isNumberDivisible(_:by:) 函数。

使用这个函数来判断下面的情况:

isPrime(6) // false
isPrime(13) // true
isPrime(8893) // true

提示 1:小于 0 的数字应该被认为不适素数。函数开始的时候检查这个情况,如果数字小于 0 就早点返回。
提示 2:使用循环来寻找除数。如果从 2 开始到数字自己之前结束,那当你找到一个除数的时候,就可以返回 false。
提示 3:如果你真的想聪明一点,循环可以只从 2 到 number 的平方根,而不是一路直到 number 自己。把它留作一个给你的练习,搞明白这是为什么。想想数字 16,平方根是 4,会很有帮助。16 的除数是 1,2,4,8 和 16。

挑战 B:递归函数

这个挑战里,我们要看看如果一个函数调用自己会发生什么,一个叫做递归(recursion)的行为。这听起来可能不太寻常,但它可以非常实用。
我们要写一个函数,计算斐波那契数列(Fibonacci sequence)中的一个值。这个数列中的任意值是前两个值的和。数列定义前两个值等于 1。也就是,fibonacci(1) =1 以及 fibonacci(2) = 1。
使用下面的声明写这个函数:

func fibonacci(number: Int) -> Int

然后,验证你写的函数是否正确,通过用下面的数字执行它:

fibonacci(1)  // = 1
fibonacci(2)  // = 1
fibonacci(3)  // = 2
fibonacci(4)  // = 3
fibonacci(5)  // = 5
fibonacci(10) // = 55

提示 1:对于小于 0 的 number 值,应该返回 0。
提示 2:要开始数列,强行让 number 等于 1 或 2 的时候返回值 1。
提示 3:对于任何其他值,你需要返回用 number - 1 和 number - 2 来调用 fibonacci 的和。

挑战源代码

https://yunpan.cn/cBWff6BiWqPbm (提取码:f63a)


介绍

欢迎来到Swift世界!Swift是一门苹果在2014年夏天发布的编程语言。从那之后,Swift发布了一个主要的版本跳跃,成为了开始在苹果平台:iOS,OS X,watchOS和tvOS开发的最简单的方式。

谁适合这篇教程

这篇教程适合懂一点编程、并且希望学习Swift的人。也许你已经为网站写过一些JavaScript代码,或者用Python写过一些简短的程序。这篇教程就是为你准备的!你会学习到编程的基本概念,同时也会成为Swift语言小能手。

如果你是赤裸裸的编程新手,这篇教程也是为你准备的!教程里贯穿有简短的锻炼和挑战来给你一些编程练习,同时测试你的知识。

需要准备什么

要看这篇教程,你需要准备如下的东西:

  • 一台运行OS X El Captian(10.11)的Mac,带有最新发布的更新并且安装了安全补丁。这样你才能够安装需要的开发工具:最新版本的Xcode。
  • Xcode 7.3 或更新的版本。Xcode是用Swift写代码的主要开发工具。最小也需要Xcode 7.3版本,因为那个版本包含Swift 2.2。你可以免费从Mac App Store下载Xcode的最新版本,这里:http://apple.co/1FLn51R

如果你还没有安装Xcode最新版本,在继续看下面的教程前要确定安装。

如何使用这篇教程

每篇教程都会介绍触手可及的话题理论,伴随大量Swift代码来示范在学习的实际的应用程序。

教程里的所有代码都是平台中立的;这意味着不是为iOS、OS X或任何其它平台而特定。代码在playgrounds里运行,你在本篇中已经学习了。

在剩下的教程里,你可以把代码在自己的playground里输入进去。这样你就可以和代码“玩耍”(play around),做一些改变立即就能看见代码运行的结果。

剩下的教程里会贯穿实际小练习,都是简短的练习,关于触手可及的主题。每篇的末尾也有挑战,会有编程问题也会有长一点的代码练习来测试你的知识。做完就能掌握大部分的Swift基础知识。

教程更新

教程会随Swift语言的更新而更新,会发布在我的简书和知乎专栏上,搜索“张嘉夫”即可关注我。

目录

本教程从一些基础工作开始来让你起步:

  • 第1篇,编程本质 & Playground基础 - 这就是,到编程世界的入门介绍!你会从电脑和编程的预览开始,然后剩余时间给Swift playground打个招呼。
  • 第2篇,变量 & 常量 - 你会学习到变量和常量,这是用来存储数据的“地方”。你也会学习数据类型以及Swift如何追踪数据类型并在代码中进行传输。
  • 第3篇,数字类型 & 操作 - 你会从基础的数字类型比如整形和浮点型数字开始,当然也包括布尔类型。也会看到一些在实际操作,从比较到算数操作如加减。
  • 第4篇,字符串 - 用字符串来存储文字-从按钮里的文字到图片的标注到这篇教程全部的文字,存储这所有的一切!你会学习到string和character类型,以及基于这些类型的一些常见操作。

在脑海中有基础数据类型后,就该用那些数据做一些事情了:

  • 第5篇,做判断 - 代码不总是直接从头运行到尾。你会学习在代码里如何做判决并且设定情况来运行某段代码。
  • 第6篇,重复步骤 - 继续不要让代码直线运行的主题,你会学习到如何使用循环来重复某些步骤。
  • 第7篇,函数 - 函数是Swift中用来构建代码的基础建筑。你会学习到如何定义函数来分组代码到可复用单元中。
  • 第8篇,闭包(Closures) - 闭包和函数很接近。你会学习到如何使用它们来轻松传递代码块。

最后一篇会回到数据:

  • 第9节,可选值 - 这篇讲可选值,Swift中的一种特殊类型,表示既有可能是一个真实的值也有可能没有值。这篇的最后你会知道为什么要用可选值以及如何安全地使用它们。

这些基础会让你快速开始Swift之路,做好接触更高级编程主题的准备。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271

推荐阅读更多精彩内容