一、元祖(Tuples)
-
Tuples have types(元祖有类型)
- 1.它是强类型的。这意味着你不能改变一个元组的类型一旦被创建,这样的代码不能编译:
-
举个栗子:已经明确了是String类型,后面又加入Int类型,就会报错
- 2.如果你不命名元素的元组,您可以访问它们使用数字计数从0,像这样:
-
举个栗子:
-
- 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: "明哥")
- 传入的参数是Int类型
inspect(value: "123")
-
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()函数,它不接受参数并返回一个闭包:
四、操作符
最简单的操作符:"="
-
举个: 这会打印Match!
但是我对操作符("=")进行重写之后,请看的代码
操作符("**")进行重写。求立方
-
介绍一个
~=
操作符。~=这个关系运算符实现的功能和包含函数一样
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) // 返回符合要求的结果,打印如图
-
打印结果:
- 一个字符串包含某个数组中的单词
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, +)
-
打印结果:
- 扩展:不仅限于Int类型,字符串也是可以的。补充一个注意点:除法的时候,必须要有浮点型或者Double类型,否则结果是0
-
例子1: 然后依次取出原来序列中的元素,根据过滤结果将它放到第一个或第二个部分中。数组变成了元祖,元祖中包含两个数组。
- 例子2::
let scores = [100, 90, 95]
let result = scores.reduce("明哥") { $0 + String($1) }
print(result);
let scores = [100, 8, 6,"欢迎你的来电"] as [Any]
let result = scores.reduce("你好!") { $0 + String(describing: $1) }
print(result);
-
例子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}) // 最短
- 例子4:reduce统计数组所有字符的个数,下面我们会学到统计数组中每一个元素的个数
let names = ["Taylor", "Paul", "Adele"]
let count = names.reduce(0) { $0 + $1.characters.count }
print(count)
-
例子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 }
- 例子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 }
-
&&的关系
• 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)})
- 举个2:
let moneyArray = [213,432,54,123,321]
let stringsArray = moneyArray.map({"\($0)明明MG"})
print(stringsArray)
-
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: "")
let encrypted = try encryptString(str: "secret information!",
withPassword: "123")
let encrypted = try encryptString(str: "secret information!",
withPassword: "12345")
"
let encrypted = try encryptString(str: "secret information!",
withPassword: "1234567")
-
Swift的错误处理有三种形式,所以三个有其用途。所有调用调用标记的函数时都使用,但有细微的不同含义:
-
1.使用try时,必须有一个catch块来处理发生的任何错误。
-
2.使用时try?如果抛出任何错误,您调用的函数将自动返回nil。您不需要捕捉它们,但您需要知道您的返回值成为可选的。
-
3.使用时try!如果抛出任何错误,函数将崩溃你的应用程序。
-
-
如何选择:
- 使用try?是比较安全的,系统会帮你处理异常,当明智地使用,会提高您的代码的可读性;使用try!一般是抛出一个错误是不太可能的 或者说 强制,我可以接受崩溃”,如果你确定它不会抛出一个错误,就用try!;使用try的话,一般和do/catch 这些闭包一起代用,自己处理异常,这个代码量是最大的。
七、Assertions(断言)
断言允许您声明某些条件必须是真的。条件是给你的,可以是复杂的,但如果它为false的话您的程序将立即停止。
-
简单举个栗子:
-
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]
八、补充数组的那些高级用法示例
我们通过一个例子学习的东西
// 统计一个数组中每一个字符个数的函数
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)
- 对数组中的元素进行过滤
- 数组的元素添加东西
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)
- 打印结果截图
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 })") // 会自动过滤空值,打印的是确定类型
到了这里,如果你还不是很清楚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)]
- 这个技术可以用于当你需要区分成功与失败之间的代码使用的可选性。任何有抛异常的函数都可用于try?去处理,它在函数转换为失败时返回nil。
- 解释:这个测试的代码将加载到一个数组中的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 })")
- 解释: filter返回了符合要求的元素。而map和flatMap返回的是条件是否是符合要求的,而且它把所有的元素判断的结果都返回在了一个数组中
- 再看一个我们之前看过的例子:
let words = ["ming", "Fearless", "Red"]
let input = "ming favorite Color is Red"
let result = words.filter { input.contains($0)}
- 再看一个例子:
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)
-
最后,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英文就是过滤的意思。所以它是带条件的筛选,它的运用场景就是数组中需要判断某个条件是否成立时经常用到