web-dev-qa-db-ja.com

Swift 3つの範囲:ベストプラクティス

昨日Xcode 8.0ベータ版をダウンロードし、その結果Swift 3をダウンロードしました。最初にしたことは、プロジェクトをSwift 3に更新しようとして、泣きそうになりました。最も重大な変更の1つは(私の意見では)Swifts Range構造体の新しい管理です。これは、特に現在のSwift構文への自動変換が範囲に対して何もしないためです。

Rangeは、RangeCountableRangeClosedRange、およびCountableClosedRangeに分割され、does範囲を使用するときに現在何が可能かを検討する際に意味があります(ただし、ほとんど不要です)。

ただし、パラメータとしてRange<Int>を受け入れるか、Range<Int>を返す関数がたくさんあります。問題は、これらの関数をたとえば0..<5または0...4で呼び出したということです(意味的に表現されることがあるためです)。もちろん、私はこれらのタイプのものを単に調整することができました。しかし、これらすべての範囲タイプに共通のインターフェースがないのはなぜですか?これらの範囲タイプごとにすべての単一関数をオーバーロードする必要があり、毎回まったく同じ操作を実行します。

Swift 3の範囲を使用するためのベストプラクティスはまだありますか?

16
borchero

実際には非常に簡単です:

Closed...Rangeは、3つのドットを使用して生成されます: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>の用途の1つを示すことができます

14
Kametrixom

閉範囲演算子

閉じた範囲演算子_(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
_

配列には4つの項目が含まれていますが、_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の例です。ランダムはしばしば範囲を扱います。

スイフト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>_として定義する必要があります

Swift への移行

RangeClosedRangeを反復することはできません(これらはもはやコレクションではありません)。単にComparableである値はインクリメントできないためです。 CountableRangeおよびCountableClosedRangeは、バウンドからStrideableを必要とし、Collectionに準拠するため、それらを反復処理できます。

15
tymac