MG--Swift3.x进阶语法学习3

一、元祖(Tuples)

  • Tuples have types(元祖有类型)

    • 1.它是强类型的。这意味着你不能改变一个元组的类型一旦被创建,这样的代码不能编译:
    • 举个栗子:已经明确了是String类型,后面又加入Int类型,就会报错


      截图
    • 2.如果你不命名元素的元组,您可以访问它们使用数字计数从0,像这样:
      • 举个栗子:


        截图.png
    • 3.元祖中又有元祖可以用..快速访问,比如singer.2.1 和 singer.address.1 。通常应该通过名称元素去寻找,这样访问它们更合理:
      • 举个栗子:写法是一样的


        元祖中又有元祖

  • Tuples and closures(元祖和闭包)

    • 1.你不能给元组添加方法,但您可以添加闭包,一个闭包添加到一个元组就像添加任何其他值。像类似调用方法一样,把参数传进去打印出来,

  • 注意:这些闭包不能访问兄弟元素,这意味着这样的代码不工作: 把print("\(lyrics)")替换为print("My name is \(first): \(lyrics)")之后
  • 2.Returning multiple values(返回多个值)

 - 举个栗子
func fetchWeather() -> (type: String, cloudCover: Int, high: Int, low: Int) {
         return ("Sunny", 50, 32, 26)
}
let weather = fetchWeather()
print(weather.type)
返回多个值



二、Generics(泛型)

  • 1.我们来看一个简单的例子 (type(of: value)打印的是类型,在Swift2.x语法对应的函数是(value.dynamicType),动态识别类型,这就是泛型的好处

func inspect<T>(value: T) {
    print("Received \(type(of: value)) with the value \(value)")
}
  • 传入的参数是String类型:
inspect(value: "明哥")
FF77A052-62A1-42A6-A098-C3F15052CA36.png
  • 传入的参数是Int类型
inspect(value: "123")
FE471D99-57C8-4714-AE3D-E078D27710BE.png

  • 2.我们自己定义一个协议,Float,Double,Int都遵守这个协议,我们就可以实现的东西。通过这个,我们可以做一个通用的数据类型的方法

extension Float: Numeric {}
extension Double: Numeric {}
extension Int: Numeric {}
protocol Numeric {
    static func *(lhs: Self, rhs: Self) -> Self  // 平方
    static func +(lhs: Self, rhs: Self) -> Self // 相加
}
func squareSomething<T: Numeric>(value: T) -> T {
    return value * value
}
func addSomething<T: Numeric>(value: T) -> T {
    return value + value
}
squareSomething(value: 1.2)
addSomething(value: 1.2)

  • 3.结构体,我们可以结合泛型创建一个数组(NSArray),然后可以快速的对其进行增删改查。字典(NSDictionary)和集合(NSCountedSet)和数组类似的方法

    • 数组

struct deque<T> {
    var array = [T]()
    mutating func pushBack(_ obj: T) {
        array.append(obj)
    }
    mutating func pushFront(_ obj: T) {
        array.insert(obj, at: 0)
    }
    mutating func popBack() -> T? {
        return array.popLast()
    }
    mutating func popFront() -> T? {
        if array.isEmpty {
            return nil
        } else {
            return array.removeFirst()
        }
    }
}
// 创建一个deque
var testDeque = deque<Int>(array: [2313,321,32,24])
testDeque.pushBack(5)
testDeque.pushFront(2)
testDeque.pushFront(1)
testDeque.pushFront(3)
testDeque.pushFront(4)
testDeque.popFront()
testDeque.popBack()
print(testDeque)
  • 集合

struct CountedSet<T: AnyObject> {
        var internalSet = NSCountedSet()
        func addObject(_ obj: T) {
            internalSet.add(obj)
        }
        func removeObject(_ obj: T) {
            internalSet.remove(obj)
        }
        func countForObject(_ obj: T) -> Int {
            return internalSet.count(for: obj)
        }
}
var countedSet = CountedSet<NSString>()
countedSet.addObject("明哥")
countedSet.addObject("明哥")
countedSet.addObject("Hello")
countedSet.countForObject("Hello")
countedSet.countForObject("明哥")
print(countedSet)
var countedSet2 = CountedSet<NSNumber>()
countedSet2.addObject(5)
print(countedSet2)



三、闭包

  • 通过学习我们自己知道,在Swift中,布尔值、数字、字符串、数组、字典、结构等等都是值类型。类是引用类型,但也闭包
  • 闭包是引用,我们从截图可以看到值是往上递增的
  • 举个实际的:下面的代码是一个createIncrementer()函数,它不接受参数并返回一个闭包:


    incrementer和incrementerCopy指向同一个指针.png

    incrementer和incrementerCopy指向不是同一个指针



四、操作符

  • 最简单的操作符:"="
  • 举个: 这会打印Match!


    操作符:"=".png
  • 但是我对操作符("=")进行重写之后,请看的代码


    对操作符("=")进行重写.png
  • 操作符("**")进行重写。求立方



    操作符重写
  • 介绍一个~=操作符。~=这个关系运算符实现的功能和包含函数一样
let test1 = (1...100).contains(42)
let test2 = 1...100 ~= 42
let dictString = "Danger, Will Robinson: mathematics failure!"
dictString ~= "计算机"
dictString.contains("计算机")
~=这个关系运算符实现的功能和包含函数一样



五、数组的高级用法

  • 1.filter()

  • 普通写法
let namees = ["Michael Paul", "Keybe brinte", "Michael Cofe",
              "ming ming", "Michael Jordan"]
let result = names.filter({ name in
      if name.hasPrefix("Michael") {
          return true
      } else {
        return false;
      }
})
print(result)   // 返回符合要求的结果,打印如图
  • 第一种写法。可以省略 return关键字
let result = namees.filter({ name in
        return name.hasPrefix("Michael")
})
print(result)  // 返回符合要求的结果,打印如图
  • 第二种写法。可以省略 return关键字
let result = namees.filter { return $0.hasPrefix("Michael") }
print(result)   // 返回符合要求的结果,打印如图
  • 打印结果:


    三种方法打印结果是一样的.png
  • 一个字符串包含某个数组中的单词
let words = ["Swift","iOS","cocoa","OSX","tvOS"]
let tweet = "This is an example tweet larking about Swift"
words.contains(where: tweet.contains) // Print(true)
// 或者这样写
tweet.characters.split(separator: " ").lazy.map(String.init).contains(where: Set(words).contains) // Print(true)
// 再或者这么写
words.filter({ tweet.contains($0)}) // 打印的是Swift

  • 2.reduce()

    • 第一种写法:
let mg_numbers = [1, 3, 5, 7, 9]
let number_result =  mg_numbers.reduce(0) { (int1, int2) -> Int in
       int1 + int2
} // 累加数组中的元素
print(number_result)
  • 第二种写法:
let number_result =  mg_numbers.reduce(0) {
       $0 + $1
} // 累加数组中的元素
  • 第三种写法:
let result = reduceNumbers.reduce(0, +)
  • 打印结果:


    打印结果.png
  • 扩展:不仅限于Int类型,字符串也是可以的。补充一个注意点:除法的时候,必须要有浮点型或者Double类型,否则结果是0

    看图
  • 例子1: 然后依次取出原来序列中的元素,根据过滤结果将它放到第一个或第二个部分中。数组变成了元祖,元祖中包含两个数组。
    97684EDF-D7DB-46AE-AFB1-DA61C169E0DD.png
  • 例子2:
let scores = [100, 90, 95]
let result = scores.reduce("明哥") { $0 + String($1) }
print(result);
打印结果.png
let scores = [100, 8, 6,"欢迎你的来电"] as [Any]
let result = scores.reduce("你好!") { $0 + String(describing: $1) }
print(result);
打印结果.png
  • 例子3:找出数组中字符最长的元素
    • reduce的写法
let names = ["Taylor", "Paul", "Adele", "mingmming", "Swift","Michael Jordan"]
let longest = names.reduce("") { $1.characters.count  >  $0.characters.count ? $1 : $0 }
  • 系统给的写法
names.max { $1.characters.count > $0.characters.count } // 最长
names.min(by: {$1.characters.count > $0.characters.count}) // 最短
找出数组中字符最长的元素.png
  • 例子4:reduce统计数组所有字符的个数,下面我们会学到统计数组中每一个元素的个数
let names = ["Taylor", "Paul", "Adele"]
let count = names.reduce(0) { $0 + $1.characters.count }
print(count)
打印结果.png
  • 例子5: reduce的多个数组转一个数组
    • reduce的写法
let numbers = [
                    [1, 1, 2],
                    [3, 5, 8],
                    [13, 21, 34] 
                  ]
let flattened: [Int] = numbers.reduce([]) { $0 + $1 }
print(flattened)
  • flatMap一句代码就搞定了
numbers.flatMap { $0 }
reduce的多个数组转一个数组.png
  • 例子6: reduce的并或的关系。第一个&&:所有名字长度大于4;第二个||:只要有一个名字的长度大于4即是真。
let names = ["Taylor", "Paul", "Adele"]
let longEnough = names.reduce(true) { $0 && $1.characters.count > 4 }
let longEnough1 = names.reduce(true) { $0 || $1.characters.count > 4 }
reduce的并或的关系.png
  • &&的关系

• false && false == false
• true && false == false
• false && true == false
• true && true == true

  • ||的关系

• false || false == false
• true || false == true
• false || true == true
• true || true == true


  • 3.map()

    • 举个1:
let π = Double(M_PI)
let max = 8.0 * π
let ds = stride(from:0.0, to:max, by:π / 6.0).map({1 - cos($0)})
Sina
  • 举个2:
let moneyArray = [213,432,54,123,321]
let stringsArray = moneyArray.map({"\($0)明明MG"})
print(stringsArray)
类型转换.png

  • 4. Split()

    • 使用 Split()方法可以分解字符串或者其他序列,追加一个任意类型的分隔符,来决定具体在什么位置进行分割。可以把字符串分割成数组
    • Split
let lipsum = "This is an example tweet larking about Swift."
let words1 = lipsum.characters.split{$0 == " "}.map(String.init)
print(words1)
  • components
let splitedArray = lipsum.components(separatedBy: " ")
print("拆分后的数组:\(splitedArray)")
拆分后的数组



六、Error(错误)

  • 我们来举个 ,密码提示错误,抛出异常
  • 异常枚举
enum PasswordError: Error {
        case Empty  // 密码为空
        case Short(minChars: Int)  // 密码不能少于
        case Obvious(message: String) // 明显的错误
}
  • 校验方法
func encryptString(str: String, withPassword password: String) throws
    -> String {
        if password.isEmpty {
           throw PasswordError.Empty
        }
        if password == "12345" {
            throw PasswordError.Obvious(message: "I have the same number on my luggage")
        }
        if password.characters.count < 4 {
            throw PasswordError.Short(minChars: 4)
        }
        let encrypted = password + str + password
        return String(encrypted.characters.reversed())
}
func testCatch() {
        do {
            let encrypted = try encryptString(str: "secret information!",
                                              withPassword: "1234567")
            print("You password is: \(encrypted)")
        } catch PasswordError.Empty {
            print("You must provide a password.")
        } catch PasswordError.Obvious(let message) {
            print("Your password is obvious: \(message)")
        } catch PasswordError.Short(let minChars) where minChars < 4 {
            print("We have a lax security policy: passwords must be at least \(minChars)")
        } catch PasswordError.Short(let minChars) where minChars < 8 {
            print("We have a moderate security policy: passwords must be at least \(minChars)")
        } catch PasswordError.Short(let minChars) {
            print("We have a serious security policy: passwords must be at least \(minChars)")
        }catch {
            print("Error")
        }
}
  • 测试:
 let encrypted = try encryptString(str: "secret information!",
                                          withPassword: "")
密码是空.png
let encrypted = try encryptString(str: "secret information!",
                                          withPassword: "123")
密码长度不够.png
let encrypted = try encryptString(str: "secret information!",
                                          withPassword: "12345")
密码已经存在.png

"

let encrypted = try encryptString(str: "secret information!",
                                          withPassword: "1234567")
显示新创建的密码.png
  • Swift的错误处理有三种形式,所以三个有其用途。所有调用调用标记的函数时都使用,但有细微的不同含义:

    • 1.使用try时,必须有一个catch块来处理发生的任何错误。
    • 2.使用时try?如果抛出任何错误,您调用的函数将自动返回nil。您不需要捕捉它们,但您需要知道您的返回值成为可选的。
    • 3.使用时try!如果抛出任何错误,函数将崩溃你的应用程序。
  • 如何选择:

    • 使用try?是比较安全的,系统会帮你处理异常,当明智地使用,会提高您的代码的可读性;使用try!一般是抛出一个错误是不太可能的 或者说 强制,我可以接受崩溃”,如果你确定它不会抛出一个错误,就用try!;使用try的话,一般和do/catch 这些闭包一起代用,自己处理异常,这个代码量是最大的。



七、Assertions(断言)

  • 断言允许您声明某些条件必须是真的。条件是给你的,可以是复杂的,但如果它为false的话您的程序将立即停止。

  • 简单举个栗子:



    Assertions(断言)种植程序.png
  • precondition(先决条件)

    • 举个栗子。我们来写一个数组中,对用位置的元素相乘的算法。这个示例的先决条件是两个数组的个数必须相等,不相等的话就终止程序。
func *(lhs: [Int], rhs: [Int]) -> [Int] {
      precondition(lhs.count == rhs.count, "Arrays were not the same size")
        var result = [Int]()
        for (index, int) in lhs.enumerated() {
            result.append(int * rhs[index])
        }
        return result
}
[1, 2] * [4, 5, 2]
[12, 5] * [5, 6]

数组个数不对应,报错.png

数组个数对应,数组相乘结果.png




八、补充数组的那些高级用法示例

  • 我们通过一个例子学习的东西

// 统计一个数组中每一个字符个数的函数
func lengthOfStrings(strings: [String]) -> [Int] {
    var result = [Int]()
    for string in strings {
        result.append(string.characters.count)
    }
    return result
}
  • 通过Map简化后统计一个数组中每一个字符个数的函数
func lengthOfStrings(strings: [String]) -> [Int] {
      return strings.map { $0.characters.count }
}
  • 经过测试两者打印的记过一样的


    测试结果

  • Map

    • 通过Map,可以快速对数组中的每一个元素进行一些操作
    • 例子:
let fruits = ["Apple", "Cherry", "Orange", "Pineapple"]
let upperFruits = fruits.map({ $0.uppercased() })
print(upperFruits)
转化成大写.png
  • 对数组中的元素进行过滤
    • 数组的元素添加东西
let mg_scores = [100, 80 ,43, 48, 85, 70, 60, 55]
let formatted = mg_scores.map { "Your score was \($0)" }
- 数组的元素是否及格
let passOrFail = mg_scores.map { $0 > 60 ? "Pass" : "Fail" }
print(passOrFail)
- 数组的元素是否在某一个范围之内
let position = [50, 60, 40]
let averageResults = position.map { 45...55 ~= $0  ? "Within average" : "Outside average" }
print(averageResults)
  - 打印结果截图
数组的元素添加东西.png

数组的元素是否及格.png

数组的元素是否在某一个范围之内.png

开平方.png
  • Map?

  • 返回可选值的方法
func fetchUsername(id: Int) -> String? {
      if id == 1989 {
          return "明哥"
      } else{
          return nil
      }
}
  • 普通写法
let username = fetchUsername(1989)
let formattedUsername: String
if let username = username {
       formattedUsername = "Welcome, \(username)!"
} else {
       formattedUsername = "Unknown user"
}
  • Map写法
let username = fetchUsername(1989)
let formattedUsername = username.map { "Welcome, \($0)!" } ?? "Unknown user"

  • forEach

    • 它跟Map有密切的联系,也遍历数组和执行的每一项功能。区别在于返回值:map()返回新数组中的项目,而foreach()什么也不返回--只是功能性的方式遍历每个项目(这样子的话速度稍微快一点点点点)。除了返回值,foreach()跟map()是一样的。

  • flatMap()

    • Map()已经很完善,但是flatMap()也有它的使用场景。当一个数组包含数组的数组。 你得到一个新的方法称为flatten()数组,数组的数组,它转换成一个数组,像这样:
let manyNumbers = [[1, 2], [3, 4], [5, 6]]
let flattened = Array(manyNumbers.joined()) // 2.x语法使用flatten() 函数
多个数组变成一个数组
  • flatMap()的功能更像是Map()和joined()的结合

let albums: [String?] = ["Fearless", nil, "Speak Now", nil, "Red"]
let albumsResult = albums.map { $0 }
print("map: \(albumsResult)")   //  打印的是可选类型
print("flatMap: \(albums.flatMap { $0 })")  // 会自动过滤空值,打印的是确定类型
结果.png

到了这里,如果你还不是很清楚flatmap()是否有用,那么继续下面
- 再看一个

let flatMapscores = ["100", "90", "Fish", "85"]
let flatMapResult = flatMapscores.flatMap { Int($0) }
let mapResult = flatMapscores.map { Int($0) }
print("flatMapscores: \(flatMapscores)")
print("flatMapResult: \(flatMapResult)")  // flatMapResult: [100, 90, 85]
print("mapResult: \(mapResult)") // mapResult: [Optional(100), Optional(90), nil, Optional(85)]
打印结果.png

- 这个技术可以用于当你需要区分成功与失败之间的代码使用的可选性。任何有抛异常的函数都可用于try?去处理,它在函数转换为失败时返回nil。

-
测试代码以及结果.png

- 解释:这个测试的代码将加载到一个数组中的somefile-1.txt、somefile-2.txt、somefile-3.txt内容等等。不存在的任何文件都会抛出一个异常,try?会转换为nil和flatmap()将忽略,所有执行的操作只需要一行代码!

  • 结论:你可以看到,flatmap()汇集了flatten(),map(),和filter() 的功能,它会对可选值和空值做特殊的处理


filter()

  • 通过学习了上面这么多函数,我想你已经晕了,这需要一段时间去熟悉之后,才会理清思路。如果你足够清醒,那就继续往下看。
  • filter()函数,如果你还不知道和上面的map()、flatMap()等有什么区别,那么我们来看下面的例子。(附上代码和打印结果,才有说服力)
let fibonacciNumbers = [1, 2, 3, 5, 8, 13, 21]
let evenFibonacci = fibonacciNumbers.filter { $0 % 2 == 0 }
print("filter: \(evenFibonacci)")
print("map: \(fibonacciNumbers.map { $0 % 2 == 0 })")
print("flatMap: \(fibonacciNumbers.flatMap { $0 % 2 == 0 })")
三者之间的区别

再一个.png
  • 解释: filter返回了符合要求的元素。而map和flatMap返回的是条件是否是符合要求的,而且它把所有的元素判断的结果都返回在了一个数组中
  • 再看一个我们之前看过的例子:
let words = ["ming", "Fearless", "Red"]
let input = "ming favorite Color is Red"
let result = words.filter { input.contains($0)}
不包含返回为空数组

返回包含的字符数组.png
  • 再看一个例子:
let words: [String?] = ["1989", nil, "Fearless", nil, "Red"]
let input = "My favorite album is Fearless"
let result = words.filter { $0 != nil }
print(result)
let result1 = words.flatMap { $0 }
print(result1)
结果截图.png
  • 最后,filter()表现奇怪的是当一个字典:你返回来的结果是元组数组,而不是一个字典。因此,你可以使用数组下标访问到一个项目,项目,0。1的键和值
let scores = ["Paul": 100, "Taylor": 95, "Adele": 90, "Michael": 85, "Justin": 60]
let goodScores = scores.filter { $1 > 85 }
goodScores
print(goodScores[0].1)
print(goodScores[1].1)
print(goodScores[2].1)
打印结果

补充:在Swift4中:字典:使用filter(),你返回来的结果是一个字典,不再是元祖。

  • 结论:filter英文就是过滤的意思。所以它是带条件的筛选,它的运用场景就是数组中需要判断某个条件是否成立时经常用到



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

推荐阅读更多精彩内容