04-Swift函数(Functions)

函数是用来完成特定任务的独立的代码块。可以给函数起一个名字,用于标识一个函数,当函数需要执行的时候,这个名字就会用于"调用"函数。
 在swift中,每个函数都有一种类型,包括函数的参数值类型和返回值类型。你可以把函数类型当做和其他普通变量类型一样处理,这就可以更简单的把函数当做其他的函数的参数,也可以从其他函数中返回函数。


一、函数定义与调用


  • 函数的定义与调用。func是函数的关键字,参数是在函数名后的括号中,指定函数返回值类型时,用返回箭头->后面跟着返回类型的方法是来表示。
/** 
func前缀即表示为函数
函数名: sayHello
参数名: persionName,类型是String 
返回值: 返回值是String类型的
*/
func sayHello(personName:String) -> String {
    let getString = "hello," + personName;
    return getString;
}
// 函数的调用
let str = sayHello("EndEvent");
print(str);
输出结果: hello,EndEvent


二、函数参数与返回值


  • 无参函数,即函数可以是没有参数的:
func speak() -> String{
    // 返回字符串
    return "hello Swift!";   
}
// 函数调用
print(speak());
输出结果: hello Swift!
  • 多参函数,多个参数以,逗号分隔:
// 参数1: name;   参数2: age;
func sayHi(name:String, age:Int) -> String {
    return "hi,我叫\(name),今年\(age)岁.";
}
print(sayHi("EndEvent", age: 20));
输出结果: hi,我叫EndEvent,今年20岁.
  • 无返回值函数,即没有返回值:
func sayBye(name:String) {
    print("Bye \(name)!");
}
sayBye("EndEvent");
// 输出结果: Bye EndEvent!
(备注: 虽然没有返回值定义,但实际`sayBye(_:)`函数依然是返回了值。这是一个特殊的返回值,叫`void`。其实返回是一个空的元组`tuple`,没有任何元素可以写成`()`)
  • 多重返回值函数,可以用元组(tuple)类型让多个值作为一个复合值返回:
// 该函数中带3个参数,返回值是一个元组
func getMaxAndMinValue(a:Int, b:Int, c:Int) -> (max:Int, min:Int){
    // 计算最大值的方法(系统自带)
    let maxValue = max(a, b, c);
    // 计算最小值的方法(系统自带)
    let minValue = min(a, b, c);
    
    return (maxValue,minValue);
}
let temp = getMaxAndMinValue(10, b: 3, c: 11);
// 因为元组成员值已命名,即可用点语法
print("maxValue:\(temp.max)");
print("minValue:\(temp.min)");
输出结果: 
maxValue:11
minValue:3
  • 可选元组返回类型。如果函数返回的元组类型,可能整个元组都是"空的",那么久可以使用* 可选的(Optional) *元组进行返回,这就表示整个元组是nil。例如(Int, Int)?(String, String)?
func getMaxAndMinValue(array: [Int]) -> (max: Int, min: 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 (currentMax, currentMin)
}
// 函数调用,不为空才打印输出
if let temp = getMaxAndMinValue([10,3,13,100,-50]) {
    print("maxValue:\(temp.max)");
    print("minValue:\(temp.min)");
}
输出结果:
maxValue:100
minValue:-50

注意: 可选元组类型如(Int, Int)?和 元组中包含可选类型如(Int?, Int?)是不一样的。可选的元组类型,整个元组是可选的,而不是元组中的每个元组值。


三、函数参数名称


函数参数都有一个* 外部参数名 和 一个 局部参数名 *。外部参数名用于在函数调用时标注传递给函数的参数,内部参数名在函数的内部使用。
 一般情况下,第一个参数省略其外部参数名,第二个以及随后的参数使用其局部参数名作为外部参数名。所有局部参数名必须是独一无二的。尽管多个参数可以有相同的外部参数名,但不同的外部参数名可以提高代码的可读性。

  • 指定外部参数名
// firstName: 第一个参数的外部参数名
// name: 第一个参数的内部参数
func sayHello (firstName name:String) {
    print("hello \(name)");
    // 错误的,因为外部参数只用于标注传递
    // print("hello \(firstName)"); 
}
// 有提供外部参数名,在调用时,必须使用外部参数名!!!
sayHello(firstName: "EndEvent");
输出结果: hello EndEvent
// 外部参数名和内部参数名可以是一样的
func sayHello (name name:String) {
    print("hello \(name)");
}
sayHello(name: "EndEvent");
输出结果: hello EndEvent
// 所有外部参数名都可以是一样的,即都为name
// 内部参数名是要在函数内使用,所以必须是不同
func sayHello (name name1:String, name name2:String) {
    print("hello \(name1)");
    print("hello \(name2)");
}
sayHello(name: "EndEvent", name: "Swift");
输出结果:
hello EndEvent
hello Swift
  • 忽略外部参数,如果不想为第二个以及后续的参数设置外部参数名,可以用_来代替一个明确的参数名:
// 第一个参数默认忽略其外部参数名称,显式写下划线是多余的
func sayHello (name1:String, _ name2:String) {
    print("hello \(name1)");
    print("hello \(name2)");
}
sayHello("EndEvent", "Swift");
输出结果: 
hello EndEvent
hello Swift
  • 默认参数值,即可以给函数体中每个参数定义默认值。如果有默认值,在调用的时候可以忽略此参数:
// 参数style是有默认值的
func goHome (name: String, style: String = "走路") {
    print("\(name)是\(style)回家的");
}
goHome("EndEvent");  // 可以忽略style参数
goHome("Swift",style: "飞");
goHome("liming", style: "开车");
输出结果:
EndEvent是走路回家的
Swift是飞回家的
liming是开车回家的

注意: 将带有默认值的参数放在函数参数列表的最后,这可以保证在函数调用时,非默认参数的顺序的一致性,同时在相同函数在不同情况下调用时显得更为清晰。

  • 可变参数,可变参数表示可以不接收或接收多个值。通过在变量类型们后加入...的方法来定义可变参数:
// 计算平均值方式,参数是可变的
func arithmeticMean(numbers:Int...) -> Float {
    var total:Float = 0;
    var sum = 0;
    for number in numbers {
        sum += number;
    }
    total = Float(sum) / Float(numbers.count);
    
    return total;
}
let total = arithmeticMean(1,2,3,4,5,6,7,8,9);
print(total);
输出结果: 5.0

注意: 一个函数中最多只能有一个可变参数。

  • 常量参数和变量参数。函数参数默认是常量,试图在函数体重修改参数值这会导致编译出错。但有时候如果想修改参数值,那么久可以将参数定义为变量参数:
func myAppend(var str:String) -> String {
    str.appendContentsOf(" --- haha");
    return str;
}
print(myAppend("EndEvent"));
输出结果: EndEvent --- haha

注意: 对于变量参数,在函数调用结束后便会消失,即是变量参数仅仅存在于函数调用的生命周期中。

  • 输入输出参数。如果想要在一个函数可以修改参数的值,并且在函数调用结束后仍然存在,即可以把这个参数定义为* 输入输出参数(In-Out Parameters) *
// 值的交换
func swapTwoInts(inout a:Int, inout b: Int) {
    let tempValue = a;
    a = b;
    b = tempValue;
}
// 定义两个变量,并初始化
var a = 10;
var b = 30;
// 调用函数交换两个变量的值
swapTwoInts(&a, b: &b);
print("a = \(a)");
print("b = \(b)");
输出结果: a = 30    b = 10

注意:输入输出参数和返回值不一样,其实这就是将变量的地址传过去,在函数体中通过变量的地址进行修改值。


四、函数类型


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

  • 使用函数类型。在swift中,使用函数类型就像使用其他类型一样:
// 相加函数
func sumFunc(a:Int, b:Int) -> Int {
    return a + b;
}
// 相乘函数
func mathFunc(a:Int, b:Int) -> Int {
    return a * b;
}
// 定义变量str,类型为(Int,Int) -> Int
var str:(Int,Int) -> Int = sumFunc;      
// 调用相加函数
print("10 + 20 = \(str(10, 20))");
// 给str这个变量赋值一个函数
str = mathFunc;
// 调用相乘函数
print("10 * 20 = \(str(10, 20))");
输出结果: 10 + 20 = 30    10 * 20 = 200
  • 函数类型作为参数类型。即你可以将(Int, Int) -> Int这样的函数类型作为另外一个函数的参数类型:
// 相乘函数
func mathFunc(a:Int, b:Int) -> Int {
    return a * b;
}
// 输出函数
func printMathFuncResult(mathFunc:(Int, Int) -> Int, a:Int, b:Int){
    print("\(a) * \(b) = \(mathFunc(a,b))");
}
// 调用输出函数
printMathFuncResult(mathFunc, a: 10, b: 10);
输出结果: 10 * 10 = 100

注意: 可能认为这和函数调用效果是一样,现在这是否多此一举?其实不是,printMathFuncResult (_:_:_:)函数作用是输出另外一个适当类型的数学函数的调用结果。即在这里只关心传入的函数类型是否正确,而不在意函数如何实现。printMathFuncResult (_:_:_:)就是以一种安全类型(type-safe)的方式,将部分功能转给调用者实现,也即是方便代码的封装。

  • 函数类型作为返回类型。可以将函数类型作为另外一个函数的返回类型:
// 实现功能: 将currentInput值变为0的过程
var currentInput = -5;
// 加操作
func stepForward(input:Int) -> Int {
    return input + 1;
}
// 减操作
func stepBackward(input:Int) -> Int {
    return input - 1;
}
// 函数选择,参数为input,返回值为'(Int) -> Int'类型
func chooseStepFunction(isBackward:Bool) -> (Int) -> Int {
    // YES:即表示currentInput值是大于0的,选择stepBackward减操作
    // NO: 即表示currentInput值是小于0的,即选择stepForward加操作
    return isBackward ? stepBackward : stepForward;
}
// 循环操作,直到currentInput为0
while currentInput != 0 {
    print("currentInput: \(currentInput)");
    // 根据currentInput值,选择对应操作的函数
    var moveNearerToZero = chooseStepFunction(currentInput > 0);
    // 调用操作
    currentInput = moveNearerToZero(currentInput);
}
print("结束操作:\(currentInput)");
输出结果:
currentInput: -5
currentInput: -4
currentInput: -3
currentInput: -2
currentInput: -1
结束操作:0


五、嵌套函数


在本节文章中你说见到函数都是* 全局函数(global functions) ,它们都是属于全局域的。但你也可以将函数定义在函数体中,而这种称之为 嵌套函数(nested functions)
嵌套函数 ,在默认情况下对外界是不可见的,但却可以被它们的 外围函数(enclosing function) *调用。并且外围函数也可以返回其所嵌套的函数,使得这个函数也可以在其他域中被调用。

// 实现功能: 输入一个值,将值变为0的过程
var currentInput = -5;
// 函数选择 - 全局函数
func chooseStepFunction(isBackwark:Bool) -> (Int) -> Int {
    // 加操作 - 嵌套函数
    func stepForward(input:Int) -> Int {
        return input + 1;
    }
    // 减操作 - 嵌套函数
    func stepBackward(input:Int) -> Int {
        return input - 1;
    }
    
    return isBackwark ? stepBackward : stepForward;
}
// 即是接受'(Int) -> Int' 类型的函数
let moveNearerToZero = chooseStepFunction(currentInput > 0);
// 循环操作,直到currentInput为0
while currentInput != 0 {
    print("currentInput: \(currentInput)");
    currentInput = moveNearerToZero(currentInput);
}
print("结束操作:\(currentInput)");
输出结果:
currentInput: -5
currentInput: -4
currentInput: -3
currentInput: -2
currentInput: -1
结束操作:0


注:xcode7.3环境

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

推荐阅读更多精彩内容

  • 86.复合 Cases 共享相同代码块的多个switch 分支 分支可以合并, 写在分支后用逗号分开。如果任何模式...
    无沣阅读 1,340评论 1 5
  • SwiftDay011.MySwiftimport UIKitprintln("Hello Swift!")var...
    smile丽语阅读 3,826评论 0 6
  • 函数是用来完成特定任务的独立的代码块。给一个函数起一个合适的名字,用来标识函数做什么,并且当函数需要执行的时候,这...
    穷人家的孩纸阅读 805评论 2 1
  • Swift 介绍 简介 Swift 语言由苹果公司在 2014 年推出,用来撰写 OS X 和 iOS 应用程序 ...
    大L君阅读 3,168评论 3 25
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young阅读 3,771评论 1 10