Array を Shuffle したい
Swift である事を実装しようと思うと、ある別の事を実装しなくてはならなくて、その実装をしようと思ったら、さらに別の実装が必要となった。それが、Array をシャッフルする事でした。そんなの標準であってもよさそうですが、標準のライブラリにはなく、どこかの勉強会で発表を見た気もするが、記憶の切れ端が見つかりません。検索して、良さげのを見つけてきても、Swift 3.0 でビルドエラーになったり、どこかのブログのコードスニペットを使ってみると、クラッシュして調べてみると、中で swap を使っている所でクラッシュしていました。いろいろ調査するよりも、自分で作ってしまえと思いこの記事を書きます。
設計方針は、「多少非効率であっても、swap を使わない」としました。
extension Array {
func shuffled() -> [Element] {
var results = [Element]()
var indexes = (0 ..< count).map { $0 }
while indexes.count > 0 {
let indexOfIndexes = Int(arc4random_uniform(UInt32(indexes.count)))
let index = indexes[indexOfIndexes]
results.append(self[index])
indexes.remove(at: indexOfIndexes)
}
return results
}
}
簡単な説明をすると、n
番目の要素を表す 0
から n-1
までの整数を生成して 中間的なArray を作ります。その中間的なArrayから要素一個をランダムに選び(m
とする)、元のArrayの m
番目の要素を新しいArrayに追加し、中間的なArrayから、m
を表す要素を削除し、これを最後まで繰り返せば、非破壊的にシャッフルされた Array が出来上がります。
もっと効率的な...なんて一瞬脳裏を横切りましたが、ここはエネルギーをかける所ではないのでこれでよしとする事にします。コード自体よりこの記事を書く方が時間がかかった事は秘密
Gist
コードは gist からも入手できます。
参考資料
How do I shuffle an array in Swift?
swiftでシャッフル関数
環境に関する表示
Apple Swift version 3.0 (swiftlang-800.0.46.2 clang-800.0.38)