雖然現在swift語言已經發展到了2.0版了,但是相信很多學習iOS開發的童鞋仍對swift語言存在各種各樣的疑問,今天小編將為大家詳細介紹swift中的范圍和區間,下面我們一起來看看吧。
Ranges
在swift語言中,范圍是用 Range 類型表達的,一個范圍就是一個索引集合。
其中,值得注意的是Range在標准庫中使用很頻繁,特別是處在集合的上下文當中時。當我們查看 Range 定義時,范圍和集合之間的緊密關系一目了然:
struct Range<Element : ForwardIndexType> : CollectionType, Indexable, ... {
...
}
在一個范圍中的元素必需遵守 ForwardIndexType 協議,同時 CollecitonType 協議中的大量功能也是基於它實現的。有一個特殊的類型用來表示集合索引的范圍,對於獲取一個集合的子集是相當有意義的。例如,我們可以使用范圍獲取一個數組的部分:
let numbers = [1,2,3,4,5,6,7,8,9]
// 1..<5 等價於 Range(start: 1, end: 5)
numbers[1..<5] // [2,3,4,5]
正如類型定義中所看到的, Range 自身遵循 CollectionType 協議,所以幾乎所有數組可以做的事情,范圍也能夠適用。比如用 for 循環遍歷元素,或者使用 contains(_:) 檢查一個值是否在這個范圍內。
雖然范圍主要適用於與其他集合配合使用,但誰也無法阻止你創建一個用於表示數字區間的 Range<Int> 。畢竟 Int 已經實現了 ForwardIndexType 協議。現在回到模式匹配問題。
我們可以用一個范圍 (Int.min..<0).contains(x) 表示 x < 0 的情況,這是完全等價的,不過執行速度巨慢。畢竟默認需要遍歷整個集合,最糟糕的情況下,將執行 9,223,372,036,854,775,808次 ,這相當耗費資源。我們可以為 Comparable (比如 Int )類型的索引提供一個更好實現:
extension Range where Element : Comparable {
func contains(element: Element) -> Bool {
return element >= startIndex && element < endIndex
}
}
(Int.min..<0).contains(-1) // true
(Int.min..<0).contains(0) // false
(Int.min..<0).contains(1) // false
這是一個非常好的練習,不過在我們案例中可有可無,因為 ~= 操作符為 Range 實現的匹配足夠高效(就像我們的 contains(_:) , Comparable 只是在索引中工作)。所以我們可以這樣的做:
Int.min..<0 ~= -1 // true
Int.min..<0 ~= 0 // false
Int.min..<0 ~= 1 // false
在這基礎上,可以寫一個 switch 語句,使用范圍查詢判斷一個數字是否大於,小於還是等於 0,對嗎?不幸地是,這並不適用。這段代碼會崩潰:
let x = 10
switch x {
case 1...Int.max: // EXC_BAD_INSTRUCTION
print("positive")
case Int.min..<0:
print("negative")
case 0:
print("zero")
default:
fatalError("Should be unreachable")
}
我們會在 case 1...Int.max 這一行中得到一個 EXC_BAD_INSTRUCTION 錯誤信息表明“fatal error: Range end index has no valid successor”。導致錯誤的原因在於: range 中的 endIndex 總是指向范圍中最后一個元素的后面。這對於半開區間(用 ..< 操作符創建)和閉合區間(用 … 操作符創建)都是一樣的,因為二者的內部實現是一樣的, a...b 事實上就是 a..<b.successor() 。
這里需要提醒大家的是,一個 Range<Int> 永遠都不能有 Int.max,這也意味着 Int.max 永遠都不會成為一個 Range<Int> 的成員,這同樣適用於其他有最大值的類型。這個限制使范圍不能滿足我們所要的需求。所以接下來讓我們來看看區間能不能滿足我們的要求。
區間
其實,在swift中,范圍和區間的是基本相同的概念構建的(一個連續元素的系列,有開始有結尾),但使用了不同的方法。范圍基於索引,因此可以是個集合,他們的大多數功能都是基本這個特性的。區間不是集合,他們的實現是依賴 Comparable 協議的。我們只可以為服從 Comparable 協議的類型創建區間類型:
protocol IntervalType {
typealias Bound : Comparable
...
}
有別於范圍的定義,區間使用 IntervalType 協議呈現,這個協議有兩個具體的實現, HalfOpenInterval 和 ClosedInterval 。兩個范圍操作符也為區間提供了重載:..< 創建一個 HalfOpenInterval 和 … 創建一個 ClosedInterval 。由於默認是重載了 Range ,所以你必須明確變量為區間類型(IntervalType):
let int1: HalfOpenInterval = 1..<5
int1.contains(5) // false
let int2: ClosedInterval = 1...5
int2.contains(5) // true
而需要注意的是 ClosedInterval 不可以為空,x…x 總是會包含 x,而 x…(x-1) 會造成運行時錯誤。
然而閉合區間可以包含一個類型的最大值。這意味着我們現在可以寫我們的 switch 語句了。重復一遍,一定要明確類型,告訴編譯器我們想要的是區間而不是范圍:
let x = 10
switch x {
case 1...Int.max as ClosedInterval:
print("positive")
case Int.min..<0 as HalfOpenInterval:
print("negative")
case 0:
print("zero")
default:
fatalError("Should be unreachable")
}
為開區間定制操作符
如果想擺脫 Int.min 和 Int.max怎么辦?這個時候,可以為開區間和閉區間自定義前綴操作符和后綴操作符,用於表示所有小於一個上邊界的值,或者大於一個下邊界的值。這樣不僅在語法上要更友善;理想情況下,這些操作符不僅適用於 Int 類型,也可以適合於其它擁有最小和最大值的類型。實現看起來應該是這個樣子:
switch x {
case 1...: // an interval from 1 to Int.max (inclusive)
print("positive")
case ..<0: // an interval from Int.min to 0 (exclusive)
print("negative")
...
}
我們需要為 ..< 和 ... 分別定義前綴和后綴的實現方式 。下面這段代碼基本是基於 Nate Cook 寫的 gist片段 。
首先,我們必須聲明需要解釋的操作符:
prefix operator ..< { }
prefix operator ... { }
postfix operator ..< { }
postfix operator ... { }
緊接着,為 Int 實現第一個運算符的方法:
/// Forms a half-open interval from `Int.min` to `upperBound`
prefix func ..< (upperBound: Int) -> HalfOpenInterval<Int> {
return Int.min..<upperBound
}
還可以讓它更通用。區間要求它的底層類型都遵循 Comparable 協議,所以使用相同的條件約束是一個很自然的選擇。但在這里我們會碰到一個問題:我們需要知道 T 類型的最小值來創建區間,但這並沒有一個通用的方法:
prefix func ..< <T : Comparable>(upperBound: T) -> HalfOpenInterval<T> {
return T.min..<upperBound // error: type 'T' has no member 'min'
}
甚至是在標准庫中的其他協議都沒有為數字(就比如 IntegerType )提供這些–定義在數字類型中的 min 和 max 屬性。
這個時候,我們可以試試這個解決方案:定義一個 MinMaxType 的自定義協議,這個協議定義了 min 和 max 兩個屬性。因為所有整數類型都有這兩個屬性,讓他們遵守新的協議就不用額外寫代碼了:
/// Conforming types provide static `max` and `min` constants.
protocol MinMaxType {
static var min: Self { get }
static var max: Self { get }
}
// Extend relevant types
extension Int : MinMaxType {}
extension Int8 : MinMaxType {}
extension Int16 : MinMaxType {}
extension Int32 : MinMaxType {}
extension Int64 : MinMaxType {}
extension UInt : MinMaxType {}
extension UInt8 : MinMaxType {}
extension UInt16 : MinMaxType {}
extension UInt32 : MinMaxType {}
extension UInt64 : MinMaxType {}
這里有一個值得牢記的技巧。任何時候,當你有幾個不相關的類型,但它們具有相同類型的一個或多個方法、屬性,你都可以創建一個新的協議給他們提供一個通用接口。
告訴我們的通用類型 T 遵守 MinMaxType 協議以使這個實現可以正常運行:
/// Forms a half-open interval from `T.min` to `upperBound`
prefix func ..< <T : Comparable where T : MinMaxType>
(upperBound: T) -> HalfOpenInterval<T> {
return T.min..<upperBound
}
這里是其他三個操作符的實現:
/// Forms a closed interval from `T.min` to `upperBound`
prefix func ... <T : Comparable where T : MinMaxType>
(upperBound: T) -> ClosedInterval<T> {
return T.min...upperBound
}
/// Forms a half-open interval from `lowerBound` to `T.max`
postfix func ..< <T : Comparable where T : MinMaxType>
(lowerBound: T) -> HalfOpenInterval<T> {
return lowerBound..<T.max
}
/// Forms a closed interval from `lowerBound` to `T.max`
postfix func ... <T : Comparable where T : MinMaxType>
(lowerBound: T) -> ClosedInterval<T> {
return lowerBound...T.max
}
添加一些測試:
(..<0).contains(Int.min) // true
(..<0).contains(-1) // true
(..<0).contains(0) // false
(...0).contains(Int.min) // true
(...0).contains(0) // true
(...0).contains(1) // false
(0..<).contains(-1) // false
(0..<).contains(0) // true
(0..<).contains(Int.max) // false
(0..<).contains(Int.max - 1) // true
(0...).contains(-1) // false
(0...).contains(0) // true
(0...).contains(Int.max) // true
回到我們的 switch 語句,現在很好地工作了:
switch x {
case 1...:
print("positive")
case ..<0:
print("negative")
case 0:
print("zero")
default:
fatalError("Should be unreachable")
}
結束語
Swift 中范圍和區間都有相似的目的,但有着不同的實現和泛型約束。范圍基於索引並且經常用於集合上下文中。這意味着范圍不能包含一個類型最大值,這就不適合用在數字的區間上。區間兼容所有的 Comparable 類型,並且沒有最大值的限制。
如果要用swift語言開發iOS應用的話,區間和范圍這兩個概念還是需要理清楚,明白什么時候用范圍、什么時候用區間,提高開發效率,提升代碼質量,一步一步邁入iOS大神行列。
相關文章:《Linux系統中CPU使用率查詢常用的5個命令》