Swift: выберите случайное значение перечисления

Я пытаюсь случайным образом выбрать значение перечисления, это моя текущая попытка:

enum GeometryClassification {

    case Circle
    case Square
    case Triangle
    case GeometryClassificationMax

}

и случайный выбор:

let shapeGeometry = ( arc4random() % GeometryClassification.GeometryClassificationMax ) as GeometryClassification

это, однако, терпит неудачу.

я получаю ошибки вроде:

'GeometryClassification' is not convertible to 'UInt32'

любые идеи о том, как это решить?

Ответы

Ответ 1

С тех пор, как был написан этот ответ, Swift получил новые функции, которые обеспечивают гораздо лучшее решение - посмотрите этот ответ.


Я не в восторге от вашего последнего случая - похоже, вы .GeometryClassificationMax исключительно для того, чтобы включить случайный выбор. Вам нужно будет учитывать этот дополнительный случай везде, где вы используете оператор switch, и он не имеет семантической ценности. Вместо этого статический метод в enum может определить максимальное значение и вернуть случайный случай, и он будет гораздо более понятным и поддерживаемым.

enum GeometryClassification: UInt32 {
    case Circle
    case Square
    case Triangle

    private static let _count: GeometryClassification.RawValue = {
        // find the maximum enum value
        var maxValue: UInt32 = 0
        while let _ = GeometryClassification(rawValue: maxValue) { 
            maxValue += 1
        }
        return maxValue
    }()

    static func randomGeometry() -> GeometryClassification {
        // pick and return a new value
        let rand = arc4random_uniform(_count)
        return GeometryClassification(rawValue: rand)!
    }
}

И теперь вы можете исчерпать enum в выражении switch:

switch GeometryClassification.randomGeometry() {
case .Circle:
    println("Circle")
case .Square:
    println("Square")
case .Triangle:
    println("Triangle")
}

Ответ 2

Поскольку вы находитесь внутри класса enum, имея ссылку на метод random(), максимальное значение явно исключает необходимость подсчитывать их каждый раз:

enum GeometryClassification: UInt32 {
    case Circle
    case Square
    case Triangle

    static func random() -> GeometryClassification {
        // Update as new enumerations are added
        let maxValue = Triangle.rawValue

        let rand = arc4random_uniform(maxValue+1)
        return GeometryClassification(rawValue: rand)!
    }
}

Ответ 3

В Swift на самом деле существует протокол для перечислений, называемый CaseIterable который, если вы добавите его в свой enum, вы можете просто ссылаться на все случаи как на коллекцию с .allCases так:

enum GeometryClassification: CaseIterable {

    case Circle
    case Square
    case Triangle

}

а затем вы можете .allCases и затем .randomElement() чтобы получить случайный

let randomGeometry = GeometryClassification.allCases.randomElement()!

Силы разворачивания требуется, потому что есть возможность перечисления, не имеющие случаев и, таким образом randomElement() будет возвращать nil.

Ответ 4

Вам нужно назначить необработанный тип для вашего перечисления. Если вы используете целочисленный тип, то значения регистра перечисления будут автоматически генерироваться начиная с 0:

enum GeometryClassification: UInt32 {  
    case Circle
    case Square
    case Triangle
    case GeometryClassificationMax
}

"В отличие от C и Objective-C, членам перечисления Swift не присваивается целочисленное значение по умолчанию при их создании." - согласно эта страница. Указание целочисленного типа позволяет ему генерировать значения обычным способом.

Затем вы можете создать случайное значение следующим образом:

let randomEnum: GeometryClassification = GeometryClassification.fromRaw(arc4random_uniform(GeometryClassification.GeometryClassificationMax.toRaw()))!

Это ужасно уродливый вызов, и все эти вызовы fromRaw и 'toRaw' довольно неэффективны, поэтому я бы рекомендовал создать случайный UInt32, который находится в нужном вам диапазоне, а затем создать GeometryClassification из этого значения

GeometryClassification.fromRaw(someRandomUInt32)

Ответ 5

Здесь мой Swift 1.2 принимает:

enum GeometryClassification : Int {
    case Circle = 0
    case Square = 1
    case Triangle = 2

    static func random() -> GeometryClassification {
        let min = MutationType.Circle.rawValue
        let max = MutationType.Triangle.rawValue
        let rand = Int.random(min: min, max: max) // Uses ExSwift!
        return self(rawValue: rand)!
    }
}

Ответ 6

Вы можете поместить все значения в массив и генерировать случайные,

extension GeometryClassification {

    static func random() -> GeometryClassification {
        let all: [GeometryClassification] = [.Circle,
                                             .Square,
                                             .Triangle,
                                             .GeometryClassificationMax]
        let randomIndex = Int(arc4random()) % all.count
        return all[randomIndex]
    }
}