我昨天下载了Xcode 8.0测试版,随后下载了Swift 3。我做的第一件事是尝试为Swift 3更新我的项目,我差点哭了。最严重的变化之一(在我看来)是Swift的Range
结构的新管理,特别是因为自动转换到当前Swift语法对范围没有任何影响。
Range
被分成Range
, CountableRange
, ClosedRange
和CountableClosedRange
,当考虑到现在使用范围时可能出现的情况时,做是有意义的(尽管它大多数是相当不必要的)。
然而:我有很多函数接受Range<Int>
作为参数或返回Range<Int>
。问题是:我用0..<5
或0...4
来调用这些函数(因为它有时在语义上更有表现力)。当然,我可以简单地调整这些东西。但是为什么这些范围类型没有一个共同的接口呢?我必须为每一个范围类型重载每一个函数,而且每次它都会执行完全相同的操作。
在Swift 3中是否有使用范围的最佳实践?
闭合范围操作符
闭合范围运算符(a...b)
定义了一个从a
到b
的范围,包括值a
和b
。a
的值不能大于b
。
当你想在一个范围内使用所有值迭代时,闭范围操作符很有用,比如for-in循环:
for index in 1...5 {
print("(index) times 5 is (index * 5)")
}
// 1 times 5 is 5
// 2 times 5 is 10
// 3 times 5 is 15
// 4 times 5 is 20
// 5 times 5 is 25
半开范围操作符
半开范围运算符(a..<b)
定义了一个从a
到b
的范围,但不包括b
。之所以说它是半开的,是因为它包含它的第一个值,但不包含它的最终值。与闭范围操作符一样,a
的值不能大于b
。如果a
的值等于b
,则结果范围为空。
半开放范围在处理从零开始的列表(如数组)时特别有用,在这些列表中,计算到(但不包括)列表的长度是很有用的:
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
print("Person (i + 1) is called (names[i])")
}
// Person 1 is called Anna
// Person 2 is called Alex
// Person 3 is called Brian
// Person 4 is called Jack
注意,数组包含四个元素,但是0..<count
只计算到3
(数组中最后一个元素的索引),因为它是一个半开的范围。
封闭范围:a...b
let myRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
半开范围:a..<b
let myRange = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
这是一个真实的SpriteKit示例,我必须使用arc4Random进行转换,这几乎适用于所有SpriteKit项目。Random通常处理范围
迅速2
Tools.swift
func randomInRange(_ range: Range<Int>) -> Int {
let count = UInt32(range.upperBound - range.lowerBound)
return Int(arc4random_uniform(count)) + range.lowerBound
}
GameScene.swift
let gap = CGFloat(randomInRange(StackGapMinWidth...maxGap))
迅速3
Tools.swift
func randomInRange(range: ClosedRange<Int>) -> Int {
let count = UInt32(range.upperBound - range.lowerBound)
return Int(arc4random_uniform(count)) + range.lowerBound
}
GameScene.swift
let gap = CGFloat(randomInRange(range: StackGapMinWidth...maxGap))
因此,如果randomInRange()
计算给定范围内的随机数,包括上界,则应定义为ClosedRange<Bound>
迁移到Swift 3
Range
和ClosedRange
不能迭代(它们不再是集合),因为仅仅是Comparable
的值不能递增。CountableRange
和CountableClosedRange
需要Strideable
从他们的边界,他们符合Collection
,所以你可以迭代他们
其实很简单:
Closed...Range
由三个点组成:0...10
。这包括下界和上界。相反的是一个非封闭范围,由0..<10
产生,不包括上界。
Countable...Range
是一个可以用带符号整数跨越的类型范围,它由0...10
或0..<10
产生。这些类型符合Sequence
协议。
几个例子:
0..<10 // CountableRange
0...Int.max // CountableClosedRange (this was not possible before Swift 3)
"A"..<"A" // Range, empty
"A"..."Z" // ClosedRange, because cannot stride through, only check if a character is in the bounds
你应该让你的方法接受一个通用的Collection
/Sequence
,这取决于你需要什么:
func test<C: Collection where C.Iterator.Element == Int>(s: C) {
print(s.first)
}
也许你可以展示一下Range<Int>