Swift 3 Ranges:最佳实践

bor*_*ero 16 swift swift3

我昨天下载了Xcode 8.0测试版,因此下载了Swift 3.我做的第一件事是尝试更新我的Swift 3项目,我几乎哭了.其中一个最严重的变化是(在我看来)Swifts Range结构的新管理,特别是因为自动转换为当前的Swift语法对范围没有任何作用.

Range被分割为Range,CountableRange,ClosedRangeCountableClosedRange确实在考虑使用范围时什么现在是可能的(虽然它主要是相当必要)时才有意义.

但是:我有很多函数接受Range<Int>as参数或返回a Range<Int>.问题是:我通过0..<5例如调用这些函数或者0...4(因为它在语义上有时更具表现力).当然,我可以简单地调整这些类型的东西.但为什么不是所有这些范围类型都有一个共同的界面?我必须为每个范围类型重载每个函数,并且每次都会执行完全相同的操作.

在Swift 3中使用范围还有什么最佳实践吗?

tym*_*mac 15

闭区域操作员

闭区域运算符(a...b)定义从ato 运行的范围b,并包括值ab.值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
Run Code Online (Sandbox Code Playgroud)

半开放式操作员

半开区间操作(a..<b)定义了从运行的范围ab,但不包括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
Run Code Online (Sandbox Code Playgroud)

请注意,该数组包含四个项目,但0..<count只计算3(数组中最后一项的索引),因为它是半开放范围.

封闭范围: a...b

let myRange = 1...3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c", "d"]
Run Code Online (Sandbox Code Playgroud)

半开放范围: a..<b

let myRange = 1..<3
let myArray = ["a", "b", "c", "d", "e"]
myArray[myRange] // ["b", "c"]
Run Code Online (Sandbox Code Playgroud)

这是一个真实的SpriteKit示例,我必须使用arc4Random进行转换,几乎所有的SpriteKit项目都是如此.随机经常处理范围.

斯威夫特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))
Run Code Online (Sandbox Code Playgroud)

斯威夫特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))
Run Code Online (Sandbox Code Playgroud)

因此,如果randomInRange()计算给定范围内的随机数,包括上限,则应将其定义为ClosedRange<Bound>

迁移到Swift 3

Range并且ClosedRange不能迭代(它们不再是集合),因为一个值只是Comparable不能递增. CountableRange并且CountableClosedRange要求Strideable从他们的界限和他们遵守,Collection以便你可以迭代他们.


Kam*_*xom 14

它实际上非常简单:

A Closed...Range通过使用三个点产生:0...10.这包括下限和上限.相反的是非闭合范围,0..<10由此产生的不包括上限.

Countable...Range是一个范围中的类型可以通过与有符号整数步幅,它是由任一产生0...100..<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
Run Code Online (Sandbox Code Playgroud)

你可能应该让你的方法接受通用Collection/ Sequence取决于你需要的东西:

func test<C: Collection where C.Iterator.Element == Int>(s: C) {
    print(s.first)
}
Run Code Online (Sandbox Code Playgroud)

也许你可以展示你的一个用途 Range<Int>

  • 你的答案中的最后一个代码片段是我正在寻找的;)谢谢 (2认同)