[Swift]函数

1. Swift的三种类型的函数:

    1) 全局函数:和C语言一样,整个文件中都可以全局调用,因此Swift和C++一样并不是完全面向对象的语言;

    2) 嵌套函数:Swift支持在函数中定义函数,函数内部定义的函数就是嵌套函数;

    3) 方法:定义在类、结构体、枚举中的函数,这体现了Swift的面向对象的属性;


2. 函数定义和调用:

    1) 所有函数,不管是全局函数、嵌套函数还是方法,定义方式都是一样的,格式如下:

func 函数名(参数列表) -> 返回值类型
{
    内容
    return 返回值
}
其中func是Swift的关键字,表示接下来要定义一个函数,函数名要符合Swift标示符定义规则;

参数列表和传统C语言参数列表不同,一般形式是:(参数名1: 类型, 参数名2: 类型, ...),多个参数之间用逗号,隔开,如果参数列表为空则表示这是一个无参函数;

-> 表示函数要返回一个值,返回值类型可以是元组(这样就实现了多值返回),如果函数不返回任何值,则“-> 返回类型”部分可以不写或者写"-> Void"或"-> ()",因为在Swift中空类型的类型名可以是Void也可以是();

    2) 几种函数定义以及调用的例子:

// 无参无返回返回
func hello1()
{
    println("hello1")
}
hello1() // 调用

// 同样也是无参无返回
func hello2() -> Void
{
    println("hello2")
}
hello2() // 调用

// 同样还是无参无返回
func hello3() -> ()
{
    println("hello3")
}
hello3() // 调用

// 单参返回String
func hello4(name: String) -> String
{
    return "Hello " + name + "!"
}
println(hello4("Peter")) // 调用

// 多参,返回两个Int的和
func add(a: Int, b: Int) -> Int
{
    return a + b
}
println(add(1, 2)) // 调用,3

// 多参,其中一个参数是元组,返回值也是元组(多值返回)
func pos(dt: Double, speed: (x: Int, y: Int)) -> (x: Int, y: Int)
{
    return (speed.x * Int(dt), speed.y * Int(dt))
}
let mov = pos(60.3, (10, -7))
println("物体的位置是:\(mov.x), \(mov.y)")

// 不给元组的成员取名也行,只不过不直观,不好调试
func pos2(dt: Double, speed: (Int, Int)) -> (Int, Int)
{
    return (speed.0 * Int(dt), speed.1 * Int(dt))
}
let mov2 = pos(60.3, (10, -7))
println("物体的位置是:\(mov2.0), \(mov2.1)")


3. 外部参数:

    1) 内部参数名和外部参数名:刚刚定义的几个函数的参数,诸如:(name: String),其中name就是函数的内部参数名,只能在函数内部使用,外界不能出现,但是我们在前面也看到过,诸如Dictionary的方法.updateValue("哈哈", forKey: 30)中第二个参数必须指定一下参数名,在把实际的参数传入,并且如果不写参数名还会报错,而这里的forKey就是外部参数名,就是指外部调用该函数的使用必须指定的参数名,而30传入给内部参数后就在函数内部通过内部参数名使用该参数;

外部参数名的好处:如果参数非常多,比如Win32的某些API有20多个参数,如果调用的时候没有明确的参数名则记忆这些参数的顺序和意义将会是一件非常头疼而繁琐的事,而外部参数名的使用可以大大减轻程序员记忆函数参数的压力;

    2) 外部参数名的定义:内部参数名是无论如何都要有的,但是外部参数名可有可无,如果无则调用的时候不需要指定参数名(就跟普通的C语言函数调用一样了),如果指定了则在调用函数时需要强制指定外部参数名,定义很简单,外部参数名写在内部参数名之前,两个参数名中间用空格隔开即可:

// 一般外部参数名要长而具体,内部参数名可以简略,外部参数名具体最主要是为了
// 面向用户,是用户更加快速学习和掌握开发的函数的使用
func area(weight w: Double, height h: Double) -> Double
{
    return w * h
}
// 调用的时候强制指定外部参数名,否则会报错!
println(area(weight: 23.2, height: 7.223))
   3) 内部参数名和外部参数名合二为一:有时内部参数名和外部参数名相同使用起来会更加方便,只要在内部参数名定义之前加#(和内部参数名之间不能用空格)就可以达到这个目的

func area(#weight: Double, #height: Double) -> Double
{
    return weight * height
}

println(area(weight: 23.44, height: 943.2))


4. 默认参数:

    1) 和C++一样也有具有默认值的参数,是在定义函数时定义,直接用赋值号指定默认值即可;

    2) 具有默认值的参数在调用函数时可以省略这些参数,但如果要修改参数则必须在调用时指定参数名,也就是说默认参数的内部名和外部名完全一样,不需要用#指定:

func test(a: Int = 1, b: Int = 2) -> Int
{
    return a + b
}
println(test()) // 3
println(test(a: 3)) // 5
// println(test(3, 5)) // Bad! 修改默认参数必须加参数名
println(test(b: 20)) // 21

func test2(a: Int = 1, b: Int = 2, c: Int) -> Int
{
    return a + b + c
}
println(test2(3)) // 6, 比C++先进,3不是赋给a,只能根据参数名来赋值
// println(test2(a: 2, 3)) // Bad! 默认参数和非默认参数之间不能隔其它默认参数!这是Swift不足的地方
println(test2(b: 3, 5)) // 即任意两个参数之间不能隔一个默认参数不传参!
默认参数当然也可以指定外部参数名,如果是这样则在调用函数并修改参数时就需要使用外部变量名了!


5. 可变参数:

和C语言可变参数一样可变参数必须是最后的且唯一的一个,可以接受任意数量的参数,但是在Swfit中可变参数的所有参数必须类型相同!定义的时候也是用同一种类型定义的:

// 可变参数也可以有外部参数名
func add(tag: String, varList nums: Int...) -> Int
{
    println(tag)
    
    var ret = 0
    for n in nums
    {
        ret += n
    }
    
    return ret
}

// 调用时外部参数名写在可变参数列表的开头,只写一次
// 其实可变参数是可变参数列表的简称!
println(add("Peter", varList: 1, 2, 3, 4, 5, 6, 7, 8, 9))

6. 变量参数——Swift的参数默认为常量的:

Swift所有参数都默认为常量,在函数体内不得修改,如果想修改,则在定义函数的时候使用var修饰参数即可:

// 既有var修饰又具有外部参数名
func test(a: Int, var b: Int, var C c: Int)
{
    // a = 7 // Bad! 默认为常量不得修改!
    b = 2 // Good!
    c = 13 // Good!
}
test(20, 90, C: 12) // 不要忘记外部参数名

7. Swift的引用传参——输入输出参数:

虽然只有类对象是引用类型的,但是也可以实现值类型数据的引用传参,在C++中引用传参的目的就是输入输出参数,因此Swift中可以用inout将变量设为输入输出参数,这样就实现了引用传参,注意,用inout修饰不用设为var,因为inout的目的就是要在函数中修改参数值:

// 也可以加外部参数名,一般规则就是修饰符都写在外部参数名之前
func test(a: Int, inout name s: String)
{
    s = "haha"
}

// let s: String = "xxxx" // 常量不能作为输入输出参数!
var s: String = "xxxx"
test(23, name: &s) // &和C++的取引用概念不同,这里仅仅是为了和inout关键字呼应,进表示引用传参的意思
println(s) // haha

8. 函数类型:

    1) 和C语言函数指针的概念类似,也可以定义常量或变量用于存放函数,这种数据的类型就是函数类型;

    2) 函数类型由两部分组成,包括参数列表(只包含类型就行了,忽略参数名),参数列表包含三要素,参数顺序、参数类型、参数个数,另一个部分是返回值类型,这点和C语言不一样的是C语言的函数签名不包含返回类型,但是在Swift中也包含返回值类型,后面将会看到,返回值类型也可以作为函数重载的依据,定义时跟普通变量一样:

// 函数类型为(Int, Int) -> Int
func add(a: Int, b: Int) -> Int
{
    return a + b
}

var fun: (Int, Int) -> Int = add
println(fun(1, 2)) // 3

func mul(a: Int, b: Int) -> Int
{
    return a * b
}
fun = mul;
println(fun(2, 5)) // 10

// 效果就是可以用一个名字完成不同的功能,类似于多态,这在C语言中也是能轻松实现的!

func add2(inout a: Int, inout b: Int) -> Int
{
    return a + b
}
// fun = add2; // Bad! inout也是参数类型的一部分
var fun2: (inout Int, inout Int) -> Int = add2;
// println(add2(2, 3)) // Bad! 必须是引用传参,参数必须是变量!
var a = 2, b = 3
println(add2(&a, &b)) // 5

func add3(var a: Int, var b: Int) -> Int
{
    return a + b
}
fun = add3
println(add3(3, 2)) // Good! var不能作为参数类型的依据

    3) 将函数类型作为返回值类型(这里使用typealias来定义类型别名,和C语言的typedef功能一样,只不过更简单易用):

func rectArea(width: Int, height: Int) -> Int
{
    return width * height
}

func triaArea(bottom: Int, height: Int) -> Int
{
    return bottom * height / 2
}

func Area(x: Int, y: Int) -> Int
{
    return x * y
}

// 定义类型别名,和C语言的typedef一样,只不过更加好用,直接赋值运算符就行了
typealias ii_i = (Int, Int) -> Int

func getArea(type: String) -> ii_i // 直接写全称(Int, Int) -> Int也ok,只不过看上去太冗长
{
    switch type
    {
    case "rect": return rectArea
    case "tria": return triaArea
    default: return Area
    }
}

println(getArea("rect")(2, 393))
println(getArea("tria")(3, 234))
println(getArea("i dont know")(23, 2353))

    4) 将函数类型作为函数参数的类型:这样就可以实现一个函数在内部调用多种类型相同但功能不同的外部函数,而不用通过繁杂的if语句再通过函数名调用这些外部函数了,可以方便的增强多态的能力

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

func mul(a: Int, b: Int) -> Int
{
    return a * b
}

func cal(f: (Int, Int) -> Int, a: Int, b: Int) -> Int
{
    return f(a, b)
}

println(cal(add, 7, 5)) // 12
println(cal(mul, 9, 10)) // 90

9. 嵌套函数:

    1) Swift允许在函数内定义函数,这些嵌套在函数内部的函数作用域局限在函数内部,外部不得访问:

func cal(opr: String, a: Int, b: Int) -> Int
{
    func add(a: Int, b: Int) -> Int { return a + b }
    func sub(a: Int, b: Int) -> Int { return a - b }
    
    switch opr
    {
    case "+": return add(a, b)
    case "-": return sub(a, b)
    default: println("error!")
    }
    
    return -1
}

println(cal("+", 1, 2)) // 3
println(cal("-", 1, 2)) // -1
println(cal("*", 1, 2)) // error! -1
// add(1, 2) // Bad! 外部不得使用!外部不可见!超出作用域
    2) 通过返回内部函数的类型来实现函数外部调用函数内部定义的嵌套函数:

就好像函数本身就是一个类对象一样,里面的嵌套函数就像是该函数的“方法”,看起来特别神奇,就好像一个函数是一个功能,而其嵌套函数就是它对外界提供的子功能

func cal(opr: String) -> (Int, Int) -> Int
{
    func add(a: Int, b: Int) -> Int { return a + b }
    func sub(a: Int, b: Int) -> Int { return a - b }
    func def(a: Int, b: Int) -> Int { return 0xffffffff }
    
    switch opr
    {
    case "+": return add
    case "-": return sub
    default: break
    }
    
    return def
}

// 只能通过调用主函数来间接达到访问嵌套函数的目的
// 仿佛是在调用函数的“方法”
println(cal("+")(1, 2)) // 3
println(cal("-")(1, 2)) // -1
println(cal("*")(1, 2)) // 4294967295

// add(1, 2) // 但是外部还是不能直接访问嵌套函数

10. 泛型:

    1) 和C++泛型感念一样,都是运行时建立类型的函数模板,效率会慢很多;

    2) 定义也是类似的,但不过Swift有泛型约束,这就比C++安全多了,这就避免用户乱定义类型导致泛型混乱的局面:

func isEqual<T: Comparable>(a: T, b: T) -> Bool // Comparable是Swift协议
// 只有遵守该可比性协议的类型才能传入,Swift基本类型(不包括集合)都属于该协议范畴
// 使用泛型而没有协议编译器是会报错的!
{
    return a == b
}

// 这里定义只有具有可比性的两种类型的数据才能传入函数
println(isEqual(1, 2)) // false
println(isEqual("haha", "haha")) // true
println(isEqual(2.3, 2.3)) // true


11. 函数重载:

    1) Swift的函数重载依据不在只有C++的函数签名那么简单了,有多了函数返回值类型,即函数返回值类型也可以作为重载依据,因此Swift重载内容包括了参数类型、参数个数和返回值类型这三项,重载时函数名必须相同,但函数类型必须不同,使用的时候一定要注意调用形式,避免歧义;

    2) 举例:

首先我们来体验一下仅有返回值类型一项不一样的重载:

func f(i: Int) -> Void { } // f1
func f(i: Int) -> Int { return 2 }  // f2

// f(1) // 仅仅是这样的调用方式会产生歧义,因为不能体现返回值类型的不一样,会导致编译出错
// 为了体现返回值类型不一样一定要这样调用
var v_void: Void = f(1) // f1
var v_none: () = f(1) // f1,Swift中Void和()都是空类型的类型名,因此两者一样
var v_int: Int = f(1) // f2
小结:调用时只要没有歧义就会发生正确的重载,因此如果重载的函数多了调用的时候一点也不用头晕,只要使得调用形式没有歧义就能正确重载;

func f(i: Int) -> Int { return i } // f1
func f(d: Double) -> Double { return d } // f2
func f(a: Int, b: Int) -> Int { return a + b } // f3
func f(s: String, i: Int) -> String { return s + "\(i)" } // f4

f(1) // f1
f(2.3) // f2
f(3, 4) // f3
f("haha", 5) // f4

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值