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]
}
}