美文网首页我爱编程
Swift 语法基础

Swift 语法基础

作者: ErlichLiu | 来源:发表于2017-05-23 23:32 被阅读13次

    Swift 基础##

    目录

    控制流的代码优化

    字符串代码注意

    swift 的数组

    字典和集合

    函数

    <a name="控制流代码优化"></a>控制流的代码优化

    如果 if ... else 的结构太过繁琐######

    那么就尽量尝试使用 guard(保证)的代码风格,注意:不要忘记return

    func buy( money: Int , price: Int , space: Int , volum: Int){
        guard money >= price else {
            print("Your money is not enough")
            return
        }
        guard space >= volum else {
            print("Your space is not enough")
            return
        }
        
        print("You can buy it")
        print("You also have \(money - price) $")
        print("You also have \(space - volum) space")
    }
    
    buy(money: 100, price: 80, space: 50, volum: 20)
    

    _ 忽略要表达的值

    在 swift 中可以是用下划线来忽略一些不关心的值。

    //使用元组---使用下划线来代替不关心的值
    let loginResult = (true , "name")
    let (isLoginSuccess , _) = loginResult
    if isLoginSuccess {
        print("Loing Success")
    }else{
        print("Login Failed")
    }
    

    元组(Tuple)的使用方法

    元组是将多个不同类型的数据,放在同一个数据类型中。
    元组可以有任意多的值,不同的值可以是不同类型。

    //声明元组---基本
    var point = (5 , 2)
    var httpResponse = ( 404 , "Not Found")
    point.0
    point.1
    
    //声明元组---显式的指定元组的类型
    var point2:(Int , Int , Int) = (1 , 2 , 3)
    var httpResponse2:(Int , String) = (200 ,  "Ok")
    
    //声明元组---为元组的各个分量赋值
    let point3 = (x: 1 , y: 2)
    point3.x
    point3.y
    
    //声明元组---显式指定类型并赋值
    let point4:( x: Int , y: Int) = ( 1 , 2)
    point4.x
    point4.y
    
    //使用元组---解包的方式使用元组中的分量
    let ( x , y) = point
    let (statusCode , statusMessage) = httpResponse
    
    //使用元组---使用下划线来代替不关心的值
    let loginResult = (true , "name")
    let (isLoginSuccess , _) = loginResult
    if isLoginSuccess {
        print("Loing Success")
    }else{
        print("Login Failed")
    }
    

    控制转移的另一种方法

    以前在面对复杂的循环结构,在转移的时候都是一层一层的break

    var gotAnswer = false
    for m in 1...300 {
        for n in 1..300{
            if m*m*m*m - n*n == 15*m*n{
                print( m , n)
                gotAnswer = true
                break        
                }
        }
        if gotAnswer  {
            break
        }
    }
    
    

    那么现在可以用另一种方式来控制转移

    //另一种方式控制转移
    finsAnswer:
    for m in 1...300 {
        for n in 1...300{
            if m*m*m*m - n*n == 15*m*n{
                print( m , n)
                //break 掉 findAnswer 这层循环
                break findAnswer
            }
        }
    }
    
    

    case where 配合使用,可以大幅度的精简代码

    //case where 配合使用可以缩短代码
    
    //在 switch 中使用 where
    let point5 = (3,3)
    switch point5 {
    case let (x,y) where x == y:
        print("It is on the line x == y")
    case let (x,y) where x == -y:
        print("It is on the line x == -y")
    case let (x, y):
        print("It is just an ordinary point")
        print("The point is \(x), \(y)")
    }
    
    //在 if 中进行模式限定的效果
    var age = 19
    if case 10...19 = age {
        print("You are a teenager")
    }
    
    //在 if 中使用where 的方法 , 在 if case where 中可以省略 where
    if case 10...19 = age , age >= 18 {
        print("You are a teenager and in a collage.")
    }
    
    
    //另一个例子
    let vector = (4,0)
    //在限定的时候才用解包 , 同时 where 还是要省略
    if case (let x, 0) = vector , x > 2 && x < 5 {
        print("It is the vector")
    }
    
    //在循环中使用 where 的方法
    for i in 1...100{
        if 1%3 == 0 {
            print(i)
        }
    }
    
    for case let i  in 1...100 where i%3 == 0 {
        print(i)
    }
    
    

    <a name="字符串代码注意"></a>字符串代码注意

    在 swift 语言中存在很多对于字符串的处理方法,并且在 swift3中存在很多的方法命名上的更新。对于 OC语言保留下来的 NSString 对于字符串的处理在某些方面比 swift 的 String 要存在优势,所以,存在要求处理繁琐的字符串的时候可以考虑使用 OC 的 NSString。

    
    import UIKit
    
    let str = "Hello, swift"
    
    // 本小节所涉及的API,大多数也经过了Swift3的化简。请大家体会Swift3对调用的字符串方法名称的化简。
    // 大小写转换
    //swift2: str.uppercaseString
    str.uppercased()
    
    //swift2: str.lowercaseString
    str.lowercased()
    
    //swift2: str.capitalizedString
    str.capitalized
    
    // 使用String的方法
    //swift2: str.containsString("Hello")
    str.contains("Hello")
    
    str.hasPrefix("Hello")
    str.hasSuffix("swift")
    
    
    // String的缺点
    let s = "one third is \(1.0/3.0)"
    print(s)
    
    // 注意:现在Swift中的String和OC中的NSString之间的界限越来越小,如使用format初始化一个String,在Swift2中已经可以了。具体代码如下:
    let ss = String(format: "one third is %.2f", 1.0/3.0)
    
    
    // as String
    let s2 = NSString(format: "one third is %.2f😀", 1.0/3.0) as String
    print(s2)
    
    
    // NSString
    var s3:NSString = "one third is 0.33😀"
    //swift2: s3.substringFromIndex(4)
    s3.substring(from: 4)
    
    //swift2: s3.substringToIndex(3)
    s3.substring(to: 3)
    
    //swift2: s3.substringWithRange(NSMakeRange(4, 5))
    s3.substring(with: NSMakeRange(4, 5))
    
    
    // String和NSString的不同
    let s4 = "😀😀😀"
    let s5:NSString = "😀😀😀"
    s4.characters.count
    s5.length
    
    
    let s6 = "   --- Hello -----    " as NSString
    //swift2: s6.stringByTrimmingCharactersInSet(NSCharacterSet(charactersInString: " -"))
    s6.trimmingCharacters(in: CharacterSet(charactersIn: " -"))
    
    //swift2: let range = s6.rangeOfString("ll")
    let range = s6.range(of: "ll")
    range.location
    range.length
    
    //swift2: s6.stringByReplacingOccurrencesOfString("He", withString: "Apo")
    s6.replacingOccurrences(of: "He", with: "Apo")
    
    

    可选型(Optional)

    可选型可以让我们在某个具体类型和 nil之间进行一个选择,可选型既可以存放一个具体类型,也可以存放nil(空)。如果想让一个变量成为可选型,必须显式声明这个变量为可选型变量。

    //必须显式声明这个这是个可选型的变量,否则,编译器不知道把类型解析为 Int? 还是 String?还是别的
    var errorCode:Int? = 404
    //这个的输出就是 Optional(404)
    print(errorCode)
    
    

    可选性的解包

    对可选型的解包使用,一来可以强制解包,但是存在很多不安全的因素。第二是才用 swift 的简洁的解包方式,可以同时解多个包。另外需要注意到,在 swift 3.0中对于解多个包的情况,每个变量都需要声明 let 或者 var ,在 if 中使用 where,在 swift3.0中也已经用英文逗号取代 where 关键字了。

    
    var errorCode:String? = "404"
    
    print(errorCode)
    
    //Unwrap 解包 !就是强制解包,但是存在风险,如果这个值不存在,那么就可能报错了
    "The errorCode is " + errorCode!
    
    //最繁琐的解包方式
    if errorCode != nil {
        "The errorCode is " + errorCode!
    }else{
        "No error"
    }
    //swift 的简化的解包方式
    if let errorCode = errorCode {
        "The errorCode is " + errorCode
    }
    
    //声明一个可选性的错误消息提示
    var errorMessage:String? = "Not found"
    
    //繁琐的解多个包方的方式
    if let errorCode = errorCode {
        if let errorMessage = errorMessage {
            print("errorCode is \(errorCode), errorMessage is \(errorMessage)")
        }
    }
    
    //swift简化的解包方式
    
    if let errorCode = errorCode , let errorMessage = errorMessage {
        print("errorCode is \(errorCode), errorMessage is \(errorMessage)")
    }
    
    //当然这里用到的是 if 语句,当然也可以使用if where 来过滤,注意在 swit3.0中 where 被替代成了英文的逗号
    
    if let errorCode = errorCode , let errorMessage = errorMessage , errorCode == "404" {
        print("Page not found")
    }
    
    

    Optional Chaining(可选型链条)

    swift 在判定条件复杂的时候,存在很多个 if let errorMessage = errorMessage { errorMessage.uppercased()}进行解包的时候,就会变得非常的繁琐,所以 swift 有了 Optional Chaining。

    
    var errorMessage:String? = "Not Found"
    
    //传统的解包方法,解包完成后对包内容的大写转换
    if let errorMessage = errorMessage{
        errorMessage.uppercased()
    }
    
    //Optional Chaining 的方法,如果 errorMessage 有值,那么就执行转换大写的操作。
    errorMessage?.uppercased()
    
    //除此之外呢,还可以搭配 if 使用
    if let errorMessage  = errorMessage?.uppercased() {
        errorMessage
    }
    
    

    nil coalesce 的风格

    当想给一个变量赋值,但是要依情况而定的时候,可能会考虑到使用 if ... else 或者三元运算符let message = message1! == nil ? "No error" : message1,那么除此之外还可以使用nil coalsece 的风格来简化代码

    
    var errorMessage:String? = nil
    
    //想给你一个值赋上初值,但是要依情况而定
    let message: String
    //最老的方式,就是先来判断errorMessage 有没有值,有的话就赋值为 errorMessage 的值,没有就赋值为“No error”
    if let errorMessage = errorMessage {
        message = errorMessage
    }else{
        message = "No error"
    }
    
    //用三目运算符的方式(这里要对 errorMessage 进行强制解包)
    let message2 = errorMessage! == nil ? "No error" : errorMessage
    
    //采用 nil coalesce 的方法
    let message3 = errorMessage?? "No error"
    

    可选型在元组中的应用

    对于元组可以声明为可选型,元组中的元素也可以声明为可选型。

    
    //对于元组中的元素声明为可选型,这时候只能是元组中的 errorMessage这个元素是可选型的
    var error1 : (errorCode: Int , errorMessage: String?) = (404 ,"Not Found")
    
    //对于元组声明为可选型,这时候只有元组是可选型的,里面的元素不是可选型。
    var error2 : (errorCode: Int , errorMessage: String)? = (404 , "Not Found")
    
    //对于元组和元组里的元素都是可选型的。
    var error3 : (errorCode: Int , errorMessage: String?)? = (404,"Not Found")
    
    

    可选型在实际情况下的应用

    //可选型的实际应用,在转换不一定能成功的地方,才用可选型。
    var ageInt: String = "xyz"
    var age = Int(ageInt)
    
    //采用 if let 的方式解包,并用 where 的方式来限定条件
    var ageInt2: String = "17"
    if let age = Int(ageInt2) , age < 20 {
        print("You are a teenager!")
    }
    

    隐式可选型

    隐式可选值其实就是把显式声明的可选值用的问号String?换成感叹号String!,隐式可选值可以在使用的时候不进行解包使用,一旦隐式可选值的值为 nil,那么程序就可能会崩溃,所以隐式可选值是不安全的。

    隐式可选型存在的意义是,用户可以暂时存放一个 nil 值,但是当程序执行之后,还可以存放相应的值。

    
    //但是隐式可选型的意义在于,在定义一些类的时候,当类刚创建的时候,有些参数是可以是没有值的,但是随着函数的执行才会有值
    var errorMessage:String!
    errorMessage  = "Not found"
    "The messge is " + errorMessage
    
    

    <a name="array"></a> swift 的数组

    数组是有序的数据集合

    对于数组的声明和初始化方法

    
    //声明数组
    var emptyArray1: [Int] = []
    var emptyArray2: Array<Int> = []
    var emptyArray3 = [Int]()
    var emptyArray4 = Array<Int>()
    //数组的初始化
    
    //初始化一个含有5个元素的并且全是0的数组
    var allZearos = [Int](repeating: 0 , count:5)
    var allZearo = Array<Int>(repeating:0 , count:5)
    
    
    

    对于数组的基本操作

    
    var numbers:Array<Int> = [1,3,4,5]
    
    //数组计数
    numbers.count
    numbers.isEmpty
    
    //索引值,注意索引越界
    numbers[2]
    
    //快速的访问数组的第一个值,但是因为不确定数组的第一个值是否存在,所以会是可选型。
    numbers.first
    numbers.last
    //在可选型数组元素中可以要进行解包
    if let firstNumber = numbers.first {
        print("This is the first \(firstNumber)")
    }
    
    //如果确定数组会存在第一个或者最后一个值存在,那么也可以是使用强制解包。
    numbers.first!
    
    //这样获取数组最后一个值的好处就是不需要进行解包
    numbers[numbers.count-1]
    
    //获取数组中最大/最小值
    numbers.min()
    numbers.max()
    
    //取出数组索引中一段的值
    numbers[1..<3]
    numbers[1...numbers.count-1]
    
    //是否包含这个元素
    numbers.contains(1)
    
    //查看索引的值在数组中的位置
    numbers.index(of: 1)
    
    //遍历数组的元素
    for number in numbers{
        print(number)
    }
    
    //遍历数组元素并获得索引
    for (i , val) in numbers.enumerated(){
        print("\(i+1): \(val)")
    }
    
    var oneToFive = [1,3,4,5]
    
    //数组之间是可以比较的
    numbers == oneToFive
    

    对于数组的增删改

    对于涉及数组索引的部分,一定要谨慎数组越界的问题

    
    //对数组的增、删、改
    var course  = ["玩转 swift 第一季" , "玩转 swift 第二季" , "玩转 swift 第三季"]
    
    //追加数组
    course.append("玩转 swift 第四季")
    
    course += ["swift 面向对象编程"]
    
    course = course + ["Swift 设计模式"]
    
    //插入数组,注意要小心数组越界
    course.insert("swift 面向协议编程", at: 4)
    
    //删除数组中的最后一个元素并返回
    course.removeLast()
    
    //删除数组中的第一个元素并返回
    course.removeFirst()
    
    //删除某一个数组元素,删除当前的数组元素并返回,还是要注意数组越界的错误
    course.remove(at: 3)
    
    //删除一定范围内的数组,同样注意不要产生数组越界
    course.removeSubrange(2..<course.count)
    
    //删除所有
    course.removeAll()
    print(course)
    
    //复制粘贴代码的时候,请先注释掉对于删除数组元素的部分。
    
    //改变数组元素的值,注意数组越界
    course[0] = "本季玩转 swift 课程已经改变"
    
    //修改一个范围内的数组元素,修改的数组元素不需要和修改区间内元素的个数一致
    course[1...3] = ["这里是范围修改的数组元素","这里也是修改范围的数组"]
    
    print(course)
    
    

    <a name="字典和集合"></a>字典和集合

    字典是存储 键 - 值的无序数据集

    字典的声明和查询

    
    //声明字典的三种方式
    var dic = ["swift": "雨燕", "python": "大蟒", "java": "爪哇岛"]
    var dic1:[String : String] = ["swift": "雨燕", "python": "大蟒", "java": "爪哇岛"]
    var dic2: Dictionary<String , String> = ["swift": "雨燕", "python": "大蟒", "java": "爪哇岛"]
    
    //声明一个空的字典
    var dic3:[String : Int] = [:]
    var dic4:Dictionary<String , Int> = [:]
    var dic5 = [String : Int]()
    var dic6 = Dictionary<String , Int>()
    
    //查询字典的值
    //使用键索引一个字典返回的是一个可选型,因为我们很容易就使用一个字典中不存在的键来访问
    dic["swift"]
    //既然返回的是一个可选型的值,那么使用的时候就需要进行解包
    
    if let value = dic["swift"] {
       print("swift's value is \(value)")
    }
    
    //查询字典的数量
    dic.count
    
    //判断字典是否为空
    dic.isEmpty
    
    //返回字典的所有键
    Array(dic.keys)
    
    //返回字典中所有值
    Array(dic.values)
    
    //打印所有字典的键,注意字典是一个无序的集合,所以打印的时候的顺序不一定跟初始化时候的一致
    for key in dic.keys{
       print(key)
    }
    //同理,可以打印打印所有的值
    for value in dic.values{
       print(value)
    }
    
    //很多时候我们同时需要键和值,那么把两个参数放在元组中,以前我们使用数组的时候,要把调用 dic.enumerated(),现在字典本身就是键值对,所以直接使用 dic,更加容易了。
    for (key , value) in dic{
       print("\(key) -> \(value)")
    }
    
    //两个字典之间可以进行值的比较,只要字典里的键值对对应,不区分顺序
    dic == dic1 
    

    字典的增删改

    //声明一个字典
    var info = ["name":"Erlich Liu","password":"Swift","occupation":"student"]
    
    //对字典的值进行修改
    info["name"] = "Liu"
    
    //使用updateValue()方法,可以返回上一个键值
    info.updateValue("Erlich Liu", forKey: "password")
    
    let oldPassword = info.updateValue("Erlich Liu", forKey: "password")
    //let newPassword = info["password"]
    if let oldPassword = oldPassword , let newPssword = info["password"] , newPssword == oldPassword {
        print("新密码与原始密码一样,可能会导致安全问题")
    }
    
    //当你检索的键不存在的时候,就是添加值,在字典中不存在数组越界的问题。
    info["course"] = "Swift"
    info.updateValue("web", forKey: "web")
    
    //当检索的不存在的键的时候,返回的值会是 nil,所以删除一个存在的键,也可以是直接检索到这个键值,然后给他赋值成 nil
    info["course"] = nil
    
    //删除一个键值,并返回当前的键值
    info.removeValue(forKey: "name")
    
    //删除所有
    info.removeAll()
    

    集合

    集合可以将一些无序的元素放在一起,其实看上去跟数组几乎一样,但是要显式的声明这个是集合。集合可以执行一些特殊的操作,比如自动除去重复的元素、可以将两个集合中元素组合起来。

    
    
    //显式声明一个集合,集合可以自动去除重复的元素
    var skillsOf:Set<String> = ["swift","OC","OC"]
    
    var set1: Set<Int> = []
    var set2 = Set<String>()
    
    //再次强调集合是不包括重复的
    var set3: Set<Int> = [1,1,1]
    set3.count
    
    //在集合里可以理解为在集合里随机挑选出一个值,但是因为在空集合里的话,是取不出来值的,所以会是一个可选值
    let s = skillsOf.first
    
    //集合中的元素也是可以进行比较的,再次强调了集合是会去除重复元素的。
    var set4: Set<Int> = [1,2]
    var set5: Set<Int> = [1,1,1,1,2,2,2]
    set4 == set5
    

    集合特有的一些操作

    
    
    //分别设计三个学生的技能
    var skillOfA:Set<String> = ["swift","OC","java"]
    var skillOfB:Set<String> = ["HTML","CSS","JavaScript"]
    var skillOfC:Set<String> = ["swift"]
    
    //向集合中添加元素
    skillOfA.insert("CSS")
    skillOfB.insert("PHP")
    skillOfC.insert("C")
    
    
    //注意在这些方法中,参数只要是 sequence 的,都可以传入一个数组或者集合的值
    
    //集合�操作:合并
    //将两个集合中的元素组合起来,使用 union() 组合是不改变每个集合的值的
    skillOfB.union(skillOfA)
    
    //使用 formUnion() 可以将skillOfB 集合的元素添加到 skillOfA的集合中
    skillOfA.formUnion(skillOfB)
    
    //交集操作:共有
    
    //用 intersection() 可以看到并返回两个集合都有的元素,但是并不会改变任何一个集合中的元素
    skillOfA.intersection(skillOfB)
    
    //用 formIntersection() 方法可以返回两者都有的元素,并将共有的元素重新赋值给第一个集合
    skillOfA.formIntersection(skillOfB)
    
    //集合操作:减法
    //使用 subtract()方法是找出 skillOfA 独有的而 skillOfB 不具备的,并不会改变任何一个集合的元素
    skillOfA.subtract(skillOfC)
    
    //使用 formSubtract()方法是找出 skillOfA 独有的,并且将 skillOfA 独有的值重新赋值给 skillOfA
    skillOfA.subtracting(skillOfC)
    
    //亦或操作:两个集合中只出现一次的那些元素
    //使用symmetricDifference()方法找出来两个集合中只出现一次的那些元素,并不会改变任何一个集合的元素
    skillOfA.symmetricDifference(skillOfB)
    //使用 formsymmetricDifferebce()方法找出两个集合中只出现过一次的元素,并且把这些元素重新赋值给第一个集合
    skillOfA.formSymmetricDifference(skillOfB)
    
    //判断是否是子集
    skillOfA.isSubset(of: skillOfB)
    
    //判断是不是真子集
    skillOfA.isStrictSubset(of: skillOfB )
    
    //判断是不是超集
    skillOfA.isSuperset(of: skillOfB)
    
    //判断是不是真超集
    skillOfA.isStrictSuperset(of: skillOfB)
    
    //判断是不是相离集合,就是这两个集合有没有共有的元素
    skillOfA.isDisjoint(with: skillOfB)
    

    选择合适的数据结构

    ==数组: 有序==

    ==集合:无序、唯一性、提供集合操作、快速查找==

    ==字典:键-值数据对==

    <a name="fun"></a>函数

    内部参数和外部参数

    内部参数和外部参数同时保证了函数内部的语义明确,又保证了函数外部分语义明确。减少了开发者需要查询文档的次数。其实外部函数就是在内部函数之前给一个命名。

    //注意函数内部的变量名称和调用函数时候的外部名称
    func sayHello(to name:String , withGreetingWord greeting:String) -> String{
        return("\(name),\(greeting)")
    }
    
    sayHello(to: "Erlich", withGreetingWord: "Good morning")
    
    

    设置可以忽略的外部参数

    但是还存在一个问题,当你不需要一个外部参数的时候,语法看上去就会显得很杂乱,所以可以选择在调用函数的时候省略参数名称。那么在定义函数参数的时候,在参数的前面添加一个下划线。

    //添加一个下划线后的参数,再调用函数的时候就会省略掉参数的名称,默认按照参数定义的顺序赋值
    func mutiple( _ num1: Int , _ num2: Int) -> Int{
        return num1 * num2
    }
    
    mutiple(2, 3)
    

    设置带有默认参数的函数

    其实就是在原有的参数部分,给一个参数赋上一个值,那么当调用这个函数的时候,可以选择忽略掉这个值。这样其实可以很大部分的缩短要编写的不同类型的构造函数,有点像是 C#语言中的多态的概念。

    
    func sayHello( _ name:String , withGreetingWord greeting:String = "Hello" , punctuation:String = "!") ->String{
        return ("\(name),\(greeting)\(punctuation)")
    }
    
    sayHello("Erlich")
    sayHello("Erlich", withGreetingWord: "你好", punctuation: "!")
    sayHello("elrich", punctuation: "!")
    
    

    设置变长的参数类型

    swift 其实又为构造函数想了一部分,当你不知道参数有多少个,但是知道参数的类型的时候,那么就就可以才用变长的参数类型,其实变长的参数类型也是很简单的,就是在参数的后面加上 ... 。这样 swift 会把输入的参数当做一个数组来处理,这样就可以实现变长的参数处理了。但是一个函数只能有一个变长的参数。

    //变长的参数类型,其实就是把变量当做数组,一个函数只能有一个变长类型
    func mean(numbers:Double ... ) -> Double{
        
        var sum:Double = 0
        for number in numbers{
            sum += number
        }
        return sum / Double(numbers.count)
    }
    
    mean(numbers: 1,2,3,4,5,6,7,8,8)
    

    可变参数(在 swift3 中已经取消掉了,实际也不建议使用这样逻辑)

    在 swift 中声明的参数其实默认都是 let 类型的,但是有的时候呢,是需要改变参数的,那么就直接在参数中显式的声明为avr就可以了。其实在 swift3中已经取消掉了自定义参数类型,但是如果有需求,还是可以实现的

    //在 swift3 中,已经不能再自定义参数类型了,但是可以在函数体内进行改变。
    //这是一个简单的将十进制转换为二进制的算法
    func  toBinary( num:Int ) ->String{
        var num = num
        var res = ""
        repeat{
            res = String(num%2) + res
            num /= 2
        }while num != 0
        return res
        
    }
    
    toBinary(num: 12)
    

    inout 类型的参数

    在 swift 中的参数,都是按引用传入的,当用inout类型的参数时候,就可以实现按值传入了。

    //这个函数是将数组里的值全部转换成 value 的值
    func initArray( arr:inout [Int] , by value:Int){
        for i in 0..<arr.count {
            arr[i] = value
        }
    }
    
    var array = [1,3,3,5,5]
    initArray(arr: &array, by: 0)
    array
    

    将函数当做变量来使用

    将函数参数化是高级函数的内容,将函数参数化可以实现更加灵活的功能,可以说函数参数化其实是在一个已知的函数上进行拓展的一种操作。

    import Cocoa
    
    //参数列表和返回值都不为空的状态
    func add( _ a:Int , _ b:Int ) -> Int{
        return a + b
    }
    let anotherAdd:(Int , Int) ->Int = add
    anotherAdd(2,4)
    
    //返回值为空的状态,当某个值为空的时候,可以使用空的()来表示,也可以使用 Void 来表示
    func sayHelloTo( _ name:String){
        print("Hello, \(name)")
    }
    
    let sayAnotherHelloTo:(String) -> Void = sayHelloTo
    sayAnotherHelloTo("Erlich")
    
    //参数列表和返回值都为空的状态
    func sayHello(){
        print("Hello")
    }
    //初次之外你还能想得到3种另外的搭配组合
    let sayAnotherHello:()->() = sayHello
    
    
    //对于参数式函数的应用场景
    var arr:[Int] = []
    for _  in 0...100 {
        arr.append(Int(arc4random()%1000))
    }
    
    //对于数组排序,但是不改变原来数组的顺序
    arr.sorted()
    arr
    //给数组排序,并改变原来数组的顺序
    arr.sort()
    arr
    
    //目前存在的排序方式都不够灵活多变,不能满足需要,那么就要用到参数式函数了
    //我们来自定义一个函数,通过参数式函数的方式来改造 sorted()函数
    func biggerNumberFirst(_ a:Int , _ b: Int) -> Bool{
        /*if a > b{
            return true
        }else{
            return false
        }*/
        return a > b
    }
    //这时就实现了从大到小的排列的函数
    arr.sorted(by: biggerNumberFirst)
    
    //这样就实现了按照字符串的大小排列了
    func cmpNumberByString( _ a:Int,_ b:Int)->Bool{
        return String(a) < String(b)
    }
    
    arr.sorted(by: cmpNumberByString)
    
    //这样就实现了谁离500更近的排序
    func near500(_ a:Int,_ b:Int)->Bool{
        return abs(500 - a) < abs(500 - b) ? true:false
    }
    
    arr.sorted(by: near500)
    

    自己构造一个参数式函数

    构造一个参数式函数的好处就是,当某个小的功能可能频繁的出现需求变更,可以不需要改变以前定义好的函数,只需要改变其中的一个当做参数的函数就可以了。swift 应该是用一种更加方便易用的方式来优化了构造函数的概念。

    import Cocoa
    
    //采用参数式函数的好处,就是在功能发生变化的时候,不需要改变每一个函数,只需要改写参数函数的一小部分
    //scores 才用 inout 类型是为了执行完函数可以改变原有的数组结构,另一个参数是一个定义分数计算方式的函数
    func changeScores( scores:inout [Int] , by changeScore:(Int) -> Int){
        for (index , score) in scores.enumerated() {
            scores[index] = changeScore(score)
        }
    }
    
    //定义了一种分数计算的方式
    func changeScore1(score:Int) -> Int{
        return Int(sqrt(Double(score)) * 10)
    }
    
    //定义了另一种分数计算方式
    func changeSocre2(score:Int) -> Int{
        return Int(Double(score) / 150 * 100)
    }
    
    //定义了两组存着分数的数组
    var scores1:[Int] = [30,48,27,40,94,69]
    var scores2 = [30,45,6,6,72,45,89,90]
    
    //采用第一种分数计算方式
    changeScores(scores: &scores1, by: changeScore1)
    //采用第另一种分数计算方式
    changeScores(scores: &scores2, by: changeSocre2)
    

    map、filter、reduce 三大函数的使用

    map 可以帮助减少上面的代码,执行一些改变数组或者别的的逻辑操作。filter 可以简单地进行过滤操作,reduce 可以简单地进行聚合操作。

    import Cocoa
    
    var score = [50,60,28,70,80,90]
    
    //用 map 操作来改变数组
    func isPassOrFail(score:Int) -> String{
        return score < 60 ? "Fail" : "Pass"
    }
    
    score.map(isPassOrFail)
    
    //用 filter 操作来过滤数组
    func fail(score:Int) -> Bool{
        return score < 60
    }
    
    score.filter(fail)
    
    //用 reduce 把数组的值聚合成一个值,最典型的就是求数组的值的和。当然,reduce 还可以聚集字符串
    func add(_ num1:Int , _ num2:Int) -> Int{
        return num1 + num2
    }
    
    score.reduce(0, add)
    score.reduce(0, +)
    

    函数的嵌套

    import Cocoa
    
    //第一种邮寄费用定价
    func tier1MailFeeByWeight(weight:Int) -> Int{
        return 1*weight
    }
    //第二种邮寄费用定价
    func tier2MailFeeByWeight(weight:Int) -> Int{
        return 3*weight
    }
    
    //计算本次邮寄费用
    func feeByUnitPrice(price:Int , weight:Int) -> Int{
        //判断选用那个邮寄费用定价
        func chooseMailFeeCalculationByWeight(weight:Int) -> (Int) -> (Int){
            return weight <= 10 ? tier1MailFeeByWeight : tier2MailFeeByWeight
        }
        let mailFeeByWeight = chooseMailFeeCalculationByWeight(weight:weight)
        return mailFeeByWeight( weight ) + price * weight
    }
    
    feeByUnitPrice(price: 100, weight: 16)
    
    

    闭包

    闭包其实和 OC 语言的 block 或者其他语言的 lambda、匿名函数的概念是一样的。虽然闭包和函数是一样的,但是某些时候使用闭包会更加的便利。

    某些函数在程序中只会使用一次,不具备复用性,那么声明一个函数就变得不太合适。

    import Cocoa
    
    var arr:[Int] = []
    for _ in 0..<100{
        arr.append(Int(arc4random()%1000))
    }
    //传统的创建一个函数来解决一次性问题
    func biggeNumberFirst(_ a:Int , _ b:Int) -> Bool{
        return a > b
    }
    arr.sorted(by: biggeNumberFirst)
    
    //使用闭包函数,对于只使用一次的函数不需要再构建一个函数
    arr.sorted(by: { (a:Int , b:Int) -> Bool in
        return a < b
    })
    

    对于上面的代码进行优化,如果闭包函数的逻辑体只有一行,可以把整个闭包都写成一行。arr.sorted(by: { (a:Int , b:Int) -> Bool in return a < b})还有很多的简化,虽然一路下来,一个闭包函数可以变得非常简化,但是并不建议一定使用最简单的办法,因为最简单的往往语义并不明确,可读性会变得很差。

    import Cocoa
    
    var arr:[Int] = []
    for _ in 0..<100{
        arr.append(Int(arc4random()%1000))
    }
    
    //使用闭包函数,对于只使用一次的函数不需要再构建一个函数
    //arr.sorted(by: { (a:Int , b:Int) -> Bool in return a < b})
    
    //它本身就是规定了传入的参数类型和返回类型,那么在简化的时候,可以直接填写参数就够了,连返回类型都不用
    //arr.sorted(by: <#T##(Int, Int) -> Bool#>)
    
    //swift 有智能类型推断,所以直接天上参数,逻辑语句就可以了,是不是非常简洁。
    arr.sorted(by:{ a , b in return a < b })
    
    //既然保证了一定会有返回值,那么连逻辑语句里的 return 都可以省略掉
    arr.sorted(by:{ a , b in a > b })
    
    //既然函数的参数都是我们自己命名的,但是函数其实自己也有命名,那么就可以这样写 $0 $1
    arr.sorted(by: { $0 > $1 })
    
    //sorted()函数本身就是在参数传入一个比较函数,那么还可以这样写
    arr.sorted(by: > )
    

    结尾闭包 Trailing Closure

    当闭包函数是传入的最后一个参数时,那么就可以把闭包函数结构放在函数的小括号外,像是一个真正的函数。

    这里的 sorted()函数本身就只有一个参数,所以很容易就可以变成这样的。只要保证闭包函数是一个方法参数里的最后一个参数,都可以变成专这样的形式arr.sorted(){ a , b in a > b }

    内容捕获

    本身一个变量并没有声明在闭包函数体内,但是闭包函数体可以使用。这就是闭包函数的内容捕获

    import Cocoa
    
    var arr:[Int] = []
    for _ in 0..<100{
        arr.append(Int(arc4random()%1000))
    }
    //本身变量并没有声明在闭包函数体内,但是闭包函数却可以正常调用。
    var num = 200
    arr.sorted(){ a,b in
        abs(a - num) > abs(b - num)
    }
    

    闭包和函数是引用类型

    闭包函数和函数都是引用类型,之前的数组,元组啊什么的都是值类型。如果是值类型,那么在做赋值操作的时候,完全是 Copy,而如果是引用类型,那在做赋值操作的时候其实会被执行赋值的函数逻辑,也就是会被再执行一遍。

    import Cocoa
    
    func runningMetersWithMetersPerDay( _ metersPerDay:Int) -> () -> Int{
        
        var totalMeters = 0
        return {
            totalMeters += metersPerDay
            return totalMeters
        }
    }
    
    //声明 A 要调用一个方法,但是runningMetersWithMetersPerDay 这个方法返回的还是一个方法
    //同时这个方法是一个无参数,返回值为 Int 的方法,那么其实planA()这个方法就是方法中的闭包函数的部分
    //所以在调用 planA 的时候,还是要加上()来表示 planA 其实是要执行的是一个方法。
    var planA = runningMetersWithMetersPerDay(2000)
    planA()
    planA()
    
    var planB = runningMetersWithMetersPerDay(5000)
    planB()
    planB()
    

    至此 swift 的基础语法就到此结束了,后面还会有进阶语法

    相关文章

      网友评论

        本文标题:Swift 语法基础

        本文链接:https://www.haomeiwen.com/subject/xznvxxtx.html