Swift 3 个范围:最佳实践
Swift 3 Ranges: Best Practices
我昨天下载了 Xcode 8.0 测试版,因此 Swift 3。我做的第一件事是尝试为 Swift 3 更新我的项目,我差点哭了。最严重的变化之一是(在我看来)Swifts Range
结构的新管理,特别是因为自动转换为当前 Swift 语法不会对范围做任何事情。
Range
分为 Range
、CountableRange
、ClosedRange
和 CountableClosedRange
, 有意义在考虑使用范围时现在可能发生的事情时(尽管这在很大程度上是不必要的)。
但是:我有很多函数接受 Range<Int>
作为参数或返回 Range<Int>
。问题是:我通过 0..<5
或 0...4
调用这些函数(因为它有时在语义上更具表现力)。当然,我可以简单地调整这些类型的东西。但为什么不是所有这些范围类型都有一个通用接口呢?我必须为这些范围类型中的每一个重载每个函数,并且它每次都会执行完全相同的操作。
在 Swift 3 中是否有使用范围的最佳实践?
其实很简单:
一个Closed...Range
是由三个点组成的:0...10
。这包括下限和上限。相反的是非封闭范围,由 0..<10
产生,不包括上限。
A 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>
的一种用途
封闭范围运算符
封闭范围运算符 (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"]
Here's a real-world SpriteKit example I had to convert using arc4Random which is in virtually all SpriteKit projects. Random often deals with ranges.
Swift 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))
Swift 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>
Range
和 ClosedRange
无法迭代(它们不再是集合),因为不能递增仅 Comparable
的值。
CountableRange
和 CountableClosedRange
要求它们的边界 Strideable
并且它们符合 Collection
以便您可以迭代它们。
我昨天下载了 Xcode 8.0 测试版,因此 Swift 3。我做的第一件事是尝试为 Swift 3 更新我的项目,我差点哭了。最严重的变化之一是(在我看来)Swifts Range
结构的新管理,特别是因为自动转换为当前 Swift 语法不会对范围做任何事情。
Range
分为 Range
、CountableRange
、ClosedRange
和 CountableClosedRange
, 有意义在考虑使用范围时现在可能发生的事情时(尽管这在很大程度上是不必要的)。
但是:我有很多函数接受 Range<Int>
作为参数或返回 Range<Int>
。问题是:我通过 0..<5
或 0...4
调用这些函数(因为它有时在语义上更具表现力)。当然,我可以简单地调整这些类型的东西。但为什么不是所有这些范围类型都有一个通用接口呢?我必须为这些范围类型中的每一个重载每个函数,并且它每次都会执行完全相同的操作。
在 Swift 3 中是否有使用范围的最佳实践?
其实很简单:
一个Closed...Range
是由三个点组成的:0...10
。这包括下限和上限。相反的是非封闭范围,由 0..<10
产生,不包括上限。
A 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>
封闭范围运算符
封闭范围运算符 (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"]
Here's a real-world SpriteKit example I had to convert using arc4Random which is in virtually all SpriteKit projects. Random often deals with ranges.
Swift 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))
Swift 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>
Range
和 ClosedRange
无法迭代(它们不再是集合),因为不能递增仅 Comparable
的值。
CountableRange
和 CountableClosedRange
要求它们的边界 Strideable
并且它们符合 Collection
以便您可以迭代它们。