【6.1 函数的灵活性】
一、笔记一
sorted: 带返回值,原来的数据源不会改变
sort: 不带返回值,就是改变自己
例如1: sorted不影响原来的值
func test1() {
let myArray = [3, 1, 2]
let res = myArray.sorted(by: <)
var myArray1 = [3, 1, 2]
myArray1.sort(by: <)
print("res:\(res) myArray:\(myArray) myArray1: \(myArray1)")
// 打印结果: res:[1, 2, 3] myArray:[3, 1, 2] myArray1: [1, 2, 3]
}
例如2:数组的元素是【元组】的排序
func test2() {
var numberStrings = [(2, "two"), (1, "one"), (3, "three")]
numberStrings.sort(by: <)
print("numberStrings: \(numberStrings)")
// numberStrings: [(1, "one"), (2, "two"), (3, "three")]
}
二、笔记二
API: lexicographicallyPrecedes
字典学上的排序, 是不是在前面
是在两个序列(简单理解为数组)之间使用
@inlinable public func lexicographicallyPrecedes<OtherSequence>(_ other: OtherSequence, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> Bool where OtherSequence : Sequence, Element == OtherSequence.Element
例如1: 字典顺序排序
func test3() {
let animals = ["elephant", "zebra", "dog"]
let res = animals.sorted { lhs, rhs in
let l = lhs.reversed()
let r = rhs.reversed()
return l.lexicographicallyPrecedes(r) // 字典上的排序 l 是不是 在 r 的前面
}
print("res:\(res)") // ["zebra", "dog", "elephant"]
}
三、笔记三
系统类:排序描述符 NSSortDescriptor的使用
例如1: 字典顺序排序
final class Person: NSObject {
@objc var firstName: String
@objc var lastName: String
@objc var yearOfBirth: Int
init(first: String, last: String, yearOfBirth: Int) {
self.firstName = first
self.lastName = last
self.yearOfBirth = yearOfBirth
}
override var description: String {
return self.firstName + " " + self.lastName + "(" + String(self.yearOfBirth) + ")" + "\n"
}
}
let people = [
Person(first: "Jo", last: "Smith", yearOfBirth: 1972),
Person(first: "Joe", last: "Smith", yearOfBirth: 1970),
Person(first: "Joe", last: "Smyth", yearOfBirth: 1971),
Person(first: "Joanne", last: "smith", yearOfBirth: 1985),
Person(first: "Joanne", last: "smith", yearOfBirth: 1979),
Person(first: "Robert", last: "Jones", yearOfBirth: 1975),
]
func test4() {
// 需求: (不区分大小写)
// 1.先按 姓 排序
// 2.再按 名 排序
// 3.再按 出生年份 排序
// 翻译 Descriptor: 描述符
let lastDescriptor = NSSortDescriptor(key: #keyPath(Person.lastName), ascending: true, selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
let firstDescriptor = NSSortDescriptor(key: #keyPath(Person.firstName), ascending: true, selector: #selector(NSString.localizedCaseInsensitiveCompare(_:)))
let yearDescriptor = NSSortDescriptor(key: #keyPath(Person.yearOfBirth), ascending: true)
let descriptors = [lastDescriptor, firstDescriptor, yearDescriptor]
let res = (people as NSArray).sortedArray(using: descriptors)
print("res: \(res.description)")
}
上面使用的是运行时接口,我们如果要使用静态函数呢?
Swift中并没有这类接口,我们可以使用函数
例如2: 怎么样使用函数来达到NSSortDescriptor的效果?
- 这一部分理解起来会比较难,可能需要一个小时
1. 我们循序渐进,先按一个属性排序
localizedCaseInsensitiveCompare : 按照当地规则无感知排序
简单理解为是字符串中使用
public func localizedCaseInsensitiveCompare(_ aString: T) -> ComparisonResult where T : StringProtocol
func test5() { // 按照当地规则无感知排序(升序)
var strings = ["Hello", "hallo", "Hallo", "hello"]
strings.sort{ $0.localizedCaseInsensitiveCompare($1) == .orderedAscending}
print("strings: \(strings)")
// 只用某一个属性排序,也是很简单
let res = people.sorted(by: {$0.yearOfBirth < $1.yearOfBirth})
print("res:\(res)")
}
2. 对2个属性(姓和名)排序,使用看起来比较聪明的方法
func test6() {
/* 问题点:
1. 每次构建一个数组是效率很低的
2. 比较操作是写死的
3. 无法对yearOfBirth进行比较
*/
let res = people.sorted(by: {p0, p1 in
let left = [p0.lastName, p0.firstName]
let right = [p1.lastName, p1.firstName]
return left.lexicographicallyPrecedes(right) {
$0.localizedCaseInsensitiveCompare($1) == .orderedAscending
}
})
print("res: \(res)")
}
3. 函数作为数据
typealias SortDescriptor<Value> = (Value, Value) -> Bool
func test7() {
let sortByYear: SortDescriptor<Person> = { $0.yearOfBirth < $1.yearOfBirth }
let sortByLastName: SortDescriptor<Person> = { $0.lastName.localizedCaseInsensitiveCompare($1.lastName) == .orderedAscending }
}
3.1 我们想想,还有没有更通用的方法呢?
/// 定义一个排序描述符函数
/// - Parameters:
/// - key: 逃逸闭包: (Value) -> Key
/// - areInIncreasingOrder: 逃逸闭包: (Key, Key) -> Bool
/// - Returns: 返回一个闭包: (Value, Value) -> Bool
func sortDescriptor<Value, Key> (key: @escaping (Value) -> Key, _ areInIncreasingOrder: @escaping (Key, Key) -> Bool) -> SortDescriptor<Value> {
return { areInIncreasingOrder(key($0), key($1)) }
}
3.2 // MARK: 思考
// key($0),key($1): 是什么?
/*
1. key的最终形态是什么?
key看起来是一个逃逸闭包,这个逃逸闭包的返回值是Key
所以说, key的最终形态是: 传入的闭包的返回值
那么,在当前案例的调用中: let sortByYearAlt: SortDescriptor = sortDescriptor(key: { $0.yearOfBirth }, <)
因为我们将返回结果泛型类型确定了: SortDescriptor, 这个泛型参数Value其实就是Person
key = { Person in
return Person.yearOfBirth
}
所以, 此案例情况下, key最终其实Person.yearOfBirth
2. areInIncreasingOrder: 是闭包 (Key, Key) -> Bool
我们这里的返回结果类型是什么呢? 是SortDescriptor => typealias SortDescriptor = (Value, Value) -> Bool
可以知道,我们需要返回 (Value, Value) -> Bool 这种类型
我们先回到调用的地方: sortDescriptor(key: { $0.yearOfBirth }, <)
这里可以写成: sortDescriptor(key: { $0.yearOfBirth }, { $0 < $1 })
key($0),表示 调用 key: @escaping (Value) -> Key
调用传入第一个参数$0,即外面使用的时候,比如排序的时候,第一个元素
同理, key($1), 表示调用的时候传入第二个元素
在这里表示传入第一个Person1, 第二个Person2
#
得到的结果是第一个 Person1.yearOfBirth
得到的结果是第二个 Person2.yearOfBirth
#
最后,返回一个闭包表达式 { areInIncreasingOrder(Person1.yearOfBirth, Person2.yearOfBirth) }
闭包类型应该是: (Key, Key) -> Bool
加{} 表示执行: { return Person1.yearOfBirth < Person2.yearOfBirth }
#
3. 总结来说: 就是传入2个参数,第一个参数表示: 获得传入一个类的某个属性的函数
第二个参数表示: 对第一个参数的结果进行比较的方法,是比较大小,还是按字母排序,还是其他运算逻辑,等等
*/
调用:
func test8() {
// 使用第一个 sortDescriptor
let sortByYearAlt: SortDescriptor<Person> = sortDescriptor(key: { $0.yearOfBirth }, < )
let res = people.sorted(by: sortByYearAlt)
print("res:\(res)")
}
3.3 为所有的遵守Comparable类型定义一个重载版本的函数
func sortDescriptor<Value, Key>(key: @escaping (Value) -> Key) -> SortDescriptor<Value> where Key: Comparable {
return { key($0) < key($1) }
}
3.4 增加支持3种排序方法的函数
/// - Parameters:
/// - key: 要比较的具体类型
/// - ascending: 是否升序,默认升序
/// - comparator: 比较器
func sortDescriptor<Value, Key>(key: @escaping (Value) -> Key,
ascending: Bool = true,
_ comparator: @escaping (Key) -> (Key) -> ComparisonResult)
-> SortDescriptor<Value> {
return { lhs, rhs in
let order: ComparisonResult = ascending
? .orderedAscending // 升序
: .orderedDescending // 降序
/*
// 1. 首先: comparator 的类型是 `(Key)` (参数部分) -> `(Key) -> ComparisonResult`(返回类型)
// 2. 所以: comparator(key(lhs))的类型就是返回的类型`(Key) -> ComparisonResult`
let res1: (Key) -> ComparisonResult = comparator(key(lhs))
// 3. 所以: 上述的res1再调用一次,返回的结果就是`ComparisonResult`类型
let res2: ComparisonResult = res1(key(rhs))
*/
// 4. 简写形式就是如下
return comparator(key(lhs))(key(rhs)) == order
}
}
上面的comparator应该是理解的难点,为了更方便的理解,我们可以再自定义一个比较器,因为上述的比较器返回的是
ComparisonResult:
@frozen public enum ComparisonResult : Int, @unchecked Sendable {
case orderedAscending = -1
case orderedSame = 0
case orderedDescending = 1
}
自定义一种年龄比较器
extension Int {
func myComparator(_ otherValue: Int) -> String {
if self > otherValue {
return "大于"
} else if self == otherValue {
return "等于"
} else {
return "小于"
}
}
}
// 自定义一种年龄比较器
func sortDescriptor<Value, Key>(key: @escaping (Value) -> Key,
ascending: Bool = true,
_ comparator: @escaping (Key) -> (Key) -> String)
-> SortDescriptor<Value> {
return { lhs, rhs in
let order: ComparisonResult = ascending
? .orderedAscending // 升序
: .orderedDescending // 降序
/*
// 1. 首先: comparator 的类型是 `(Key)` (参数部分) -> `(Key) -> String`(返回类型)
// 2. 所以: comparator(key(lhs))的类型就是返回的类型`(Key) -> String`
let res1: (Key) -> String = comparator(key(lhs))
// 3. 所以: 上述的res1再调用一次,返回的结果就是`String`类型
let res2: String = res1(key(rhs))
*/
// 4. 简写形式就是如下
return comparator(key(lhs))(key(rhs)) == "大于"
}
}
下面是两种调用方法的实现和打印结果:
func test9() {
// 使用重载版本的 sortDescriptor
let sortByYearAlt: SortDescriptor<Person> = sortDescriptor(key: { $0.yearOfBirth })
// 使用传比较器的 ( $0.localizedCaseInsensitiveCompare($1))
let sortByFirstName: SortDescriptor<Person> = sortDescriptor(key: {$0.firstName}, String.localizedCaseInsensitiveCompare)
let res = people.sorted(by: sortByFirstName)
print(res)
/*
打印结果 res:
[Jo Smith(1972)
, Joanne smith(1985)
, Joanne smith(1979)
, Joe Smith(1970)
, Joe Smyth(1971)
, Robert Jones(1975)
]
*/
// 传自定义的比较器
let sortMy: SortDescriptor<Person> = sortDescriptor(key: {$0.yearOfBirth}, Int.myComparator)
let res1 = people.sorted(by: sortMy)
print(res1)
/*
打印结果res1:
[Joanne smith(1985)
, Joanne smith(1979)
, Robert Jones(1975)
, Jo Smith(1972)
, Joe Smyth(1971)
, Joe Smith(1970)
]
*/
}
- 将多种比较器结合起来,根据优先级排序(比如说,
a.先按姓排名,
b.如果姓一样,再按名排名,
c.如果姓名都一样,再按年龄排名
)
func combine<Value>(sortDescriptors: [SortDescriptor<Value>]) -> SortDescriptor<Value> {
return { lhs, rhs in
for areInIncreasingOrder in sortDescriptors {
if areInIncreasingOrder(lhs, rhs) { return true }
if areInIncreasingOrder(rhs, lhs) { return false }
}
return false
}
}
调用:
func test10() {
let sortByYearAlt: SortDescriptor<Person> = sortDescriptor(key: { $0.yearOfBirth })
let sortByFirstName: SortDescriptor<Person> = sortDescriptor(key: {$0.firstName}, String.localizedCaseInsensitiveCompare)
let sortByLastName: SortDescriptor<Person> = sortDescriptor(key: {$0.lastName}, String.localizedCaseInsensitiveCompare)
let combined: SortDescriptor<Person> = combine(sortDescriptors: [sortByLastName, sortByFirstName, sortByYearAlt])
let res = people.sorted(by: combined)
print(res)
}
返回二阶函数
/// 返回一个二阶函数
/// - Parameter compare: 传入一个比较器(二阶函数)
/// - Returns: 返回一个比较器(也是二阶函数),返回的比较器可以接受可选值
func lift<A>(_ compare: @escaping (A) -> (A) -> ComparisonResult) -> (A?) -> (A?) -> ComparisonResult {
return { lhs in // 解析后成为: 一阶函数
{ rhs in // 再一次解析普通函数
switch (lhs, rhs) {
case (nil, nil):
return .orderedSame
case (nil, _):
return .orderedAscending
case (_, nil):
return .orderedDescending
case let (l?, r?): //也可以写成 (let l?, let r?), 这里简写成 let (l?, r?)
return compare(l)(r)
default: fatalError()
}
}
}
}
func test11() {
let files = ["one.", "file.h", "file.c", "test.h"]
let lcic = lift(String.localizedCaseInsensitiveCompare)
let result = files.sorted(by: sortDescriptor(key: { $0.components(separatedBy: ".").last }, lcic))
print(result)
}
最后总结:xxx
xxx