[WWDC18] Swift 4.2からはランダム値生成にRandomNumberGeneratorを使おう #WWDC18

Swift 4.2から使えるRandomNumberGeneratorを使ったランダム値生成方法についてご紹介します。
2018.06.06

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

本記事は Apple からベータ版として公開されているドキュメントを情報源としています。 そのため、正式版と異なる情報になる可能性があります。ご留意の上、お読みください。

はじめに

WWDC18のセッション、「What’s New in Swift」の中で、Swift 4.2で実装されたRandom Unification SE-0202の話があったので、簡単に試してみました。

検証環境

  • macOS High Sierra 10.13.4
  • Xcode Version 10.0 beta (10L176w)

これまでのランダム値生成

これまでSwiftでランダム値を生成するには、arc4random_uniformなどを使って行っていました。ただし、この方法には以下のような問題がありました。

  • importされたCのAPI
  • システム(DarwinかLinuxかなど)に依存した処理

Swift 4.2からのランダム値生成

上記のような問題があったことから、Random Unification SE-0202が提案され、Swift 4.2で実装されました。

IntやFloatのrandom関数を使う

IntやFloatではrandom関数が使えるようになったので以下のように簡単に乱数を生成できます。

// 1から10までのIntを生成
let iValue = Int.random(in: 1 ... 10)
print(iValue)

// 1から3未満のFloatを生成
let fValue = Float.random(in: 1 ..< 3)
print(fValue)

コレクションから要素をランダムに取り出したい時はrandomElement関数を使う

// コレクションからランダムに値を取得
let animals = ["cat", "dog", "pig", "horse", "kangaroo"]
animals.randomElement()! // 配列が空ではないことが自明なので強制アンラップしてよい

var values = [String]()

if let randomValue = values.randomElement() {
    // 空のコレクションの場合はrandomElement()はnilを返すので、if let等を使いアンラップする
}

コレクションの要素をシャッフルしたい場合はshuffled関数を使う

// シャッフル
let shuffledAnimals = animals.shuffled()
print(shuffledAnimals) // ex. ["horse", "pig", "dog", "kangaroo", "cat"]

独自のRandomNumberGeneratorを作って使いたい場合はRandomNumberGeneratorプロトコルに準拠したstruct(class)を作成する

上述のrandom関数やrandomElement関数は明示的にRandomNumberGeneratorを指定していません。この場合はデフォルトのRandom.defaultが使われます。

もし独自のRandomNumberGeneratorを使いたい場合はRandomNumberGeneratorプロトコルに準拠したstruct(class)を作成します。

struct MyRNG: RandomNumberGenerator {
    // RandomNumberGeneratorの実装
}

以下はRandomNumberGeneratorの定義です。

public protocol RandomNumberGenerator {

    /// Returns a value from a uniform, independent distribution of binary data.
    ///
    /// - Returns: An unsigned 64-bit random value.
    public mutating func next() -> UInt64
}

extension RandomNumberGenerator {

    /// Returns a value from a uniform, independent distribution of binary data.
    ///
    /// - Returns: A random value of `T`. Bits are randomly distributed so that
    ///   every value of `T` is equally likely to be returned.
    public mutating func next<T>() -> T where T : FixedWidthInteger, T : UnsignedInteger

    /// Returns a random value that is less than the given upper bound.
    ///
    /// - Parameter upperBound: The upper bound for the randomly generated value.
    /// - Returns: A random value of `T` in the range `0..<upperBound`. Every
    ///   value in the range `0..<upperBound` is equally likely to be returned.
    public mutating func next<T>(upperBound: T) -> T where T : FixedWidthInteger, T : UnsignedInteger
}

そして作ったRandomNumberGeneratorを各関数のパラメーターに渡します。

var myRNG = MyRNG()
let value = Int.random(in: 1 ..< 5, using: &myRNG)

おわりに

Swift 4.2からのランダム値生成方法についてご紹介しました。 ランダム値を生成する機会がありましたらこの記事を思い出してみてください。