以下の関数をSwift 3
に変換するにはどうすればよいですか?現在、Binary operator '..<' cannot be applied to operands of type 'Int' and 'Self.IndexDistance'
エラーが発生しています。
extension MutableCollection where Index == Int {
/// Shuffle the elements of `self` in-place.
mutating func shuffleInPlace() {
// empty and single-element collections don't shuffle
if count < 2 { return }
for i in 0..<count - 1 { //error takes place here
let j = Int(arc4random_uniform(UInt32(count - i))) + i
guard i != j else { continue }
swap(&self[i], &self[j])
}
}
}
count
は、2つのコレクションインデックス間の距離を表すタイプであるIndexDistance
を返します。 IndexDistance
はSignedInteger
である必要がありますが、Int
である必要はなく、Index
と異なっていてもかまいません。したがって、範囲_0..<count - 1
_を作成することはできません。
解決策は、_0
_とstartIndex
の代わりにendIndex
とcount
を使用することです。
_extension MutableCollection where Index == Int {
/// Shuffle the elements of `self` in-place.
mutating func shuffle() {
// empty and single-element collections don't shuffle
if count < 2 { return }
for i in startIndex ..< endIndex - 1 {
let j = Int(arc4random_uniform(UInt32(endIndex - i))) + i
if i != j {
swap(&self[i], &self[j])
}
}
}
}
_
もう1つの利点は、配列slices(最初の要素のインデックスが必ずしもゼロではない場合)でも正しく機能することです。
新しい "Swift API設計ガイドライン" によれば、shuffle()
は変更シャッフルメソッドの「適切な」名前であり、shuffled()
はnon配列を返す-mutatingの対応物:
_extension Collection {
/// Return a copy of `self` with its elements shuffled
func shuffled() -> [Iterator.Element] {
var list = Array(self)
list.shuffle()
return list
}
}
_
更新:A(さらに一般的)Swift 3バージョンが追加されました Swiftで配列をシャッフルするにはどうすればよいですか? その間。
Swift 4(Xcode 9)の場合、swap()
関数の呼び出しをswapAt()
コレクションのメソッド。また、Index
型の制限は不要になりました。
_extension MutableCollection {
/// Shuffle the elements of `self` in-place.
mutating func shuffle() {
for i in indices.dropLast() {
let diff = distance(from: i, to: endIndex)
let j = index(i, offsetBy: numericCast(arc4random_uniform(numericCast(diff))))
swapAt(i, j)
}
}
}
_
swapAt
の詳細については、 SE-0173 Add MutableCollection.swapAt(_:_:)
を参照してください。
Swift 4.2(Xcode 10、現在ベータ版)、 SE-0202 Random Unification 、shuffle()
およびshuffled()
は、Swift標準ライブラリの一部です。
Gamekitにはfisher-yatesシャッフルがあります。
import GameKit
let unshuffledArray = [1,2,3,4]
let shuffledArray = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: unshuffledArray)
print(shuffledArray)
ランダムシードを渡して保存することもできるため、シミュレーションを再作成する必要がある場合に備えて、同じシードを提供するたびに、擬似ランダムシャッフル値の同じシーケンスを取得できます。
import GameKit
let unshuffledArray = [1,2,3,4]
let randomSource = GKLinearCongruentialRandomSource(seed: 1)
let shuffledArray = randomSource.arrayByShufflingObjects(in: unshuffledArray)
//Always [1,4,2,3]
print(shuffledArray)
これを一般的なコレクションに拡張するのではなく、単に配列をシャッフルすることをお勧めします。
extension Array {
mutating func shuffle () {
for i in (0..<self.count).reversed() {
let ix1 = i
let ix2 = Int(arc4random_uniform(UInt32(i+1)))
(self[ix1], self[ix2]) = (self[ix2], self[ix1])
}
}
}
これには、GameplayKitフレームワークのNSArray Extensionを使用できます。
import GameplayKit
extension Collection {
func shuffled() -> [Iterator.Element] {
let shuffledArray = (self as? NSArray)?.shuffled()
let outputArray = shuffledArray as? [Iterator.Element]
return outputArray ?? []
}
mutating func shuffle() {
if let selfShuffled = self.shuffled() as? Self {
self = selfShuffled
}
}
}
// Usage example:
var numbers = [1,2,3,4,5]
numbers.shuffle()
print(numbers) // output example: [2, 3, 5, 4, 1]
print([10, "hi", 9.0].shuffled()) // output example: [hi, 10, 9]