Random 如何选择随机枚举值

Random 如何选择随机枚举值,random,swift,enums,Random,Swift,Enums,我正在尝试随机选择一个枚举值: enum GeometryClassification { case Circle case Square case Triangle case GeometryClassificationMax } 以及随机选择: let shapeGeometry = ( arc4random() % GeometryClassification.GeometryClassificationMax ) as GeometryClassif

我正在尝试随机选择一个枚举值:

enum GeometryClassification {

    case Circle
    case Square
    case Triangle
    case GeometryClassificationMax

}
以及随机选择:

let shapeGeometry = ( arc4random() % GeometryClassification.GeometryClassificationMax ) as GeometryClassification
但它失败了

我会遇到如下错误:

'GeometryClassification' is not convertible to 'UInt32'

如何解决此问题?

您需要为枚举分配一个原始类型。如果使用整数类型,则枚举案例值将从0开始自动生成:

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

与C和Objective-C不同,Swift枚举成员在创建时未指定默认整数值

指定整数类型可以让它知道如何以通常的方式生成值

然后可以生成如下所示的随机值:

let randomEnum: GeometryClassification = GeometryClassification.fromRaw(arc4random_uniform(GeometryClassification.GeometryClassificationMax.toRaw()))!
这是一个非常丑陋的调用,所有这些
fromRaw
toRaw
调用都非常不优雅,因此我建议首先生成一个在您想要的范围内的随机UInt32,然后根据该值创建一个
Geometryclassion

GeometryClassification.fromRaw(someRandomUInt32)

自编写此答案以来,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)!
    }
}
现在,您可以在
开关
语句中耗尽
枚举

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

由于您在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)!
    }
}

以下是我的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)!
    }
}

您可以将所有值放入数组并生成随机值

extension GeometryClassification {

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

在Swift中,实际上有一个用于枚举的协议,名为
caseitrable
,如果您将其添加到枚举中,您可以将所有案例作为集合引用
。所有案例
,如下所示:

enum GeometryClassification: CaseIterable {

    case Circle
    case Square
    case Triangle

}
然后你可以
.allCases
然后
.randomElement()
得到一个随机的

let randomGeometry = GeometryClassification.allCases.randomElement()!
强制展开是必需的,因为枚举可能没有大小写,因此
randomElement()
将返回
nil

对于Swift 5,有“”:

enum工作日:caseitrable{
案件星期日、星期一、星期二、星期三、星期四、星期五、星期六
静态func随机(使用生成器:inout G)->工作日{
返回工作日.allCases.randomElement(使用:&生成器)!
}
静态func random()->工作日{
var g=SystemRandomNumberGenerator()
返回Weekday.random(使用:&g)
}
}

我使用answer编写了一个全局扩展。享受:)


最简单的方法是创建一个全局扩展:

extension CaseIterable {
    static func randomElement() -> AllCases.Element {
        guard Self.allCases.count > 0 else {
            fatalError("There must be at least one case in the enum")
        }
        return Self.allCases.randomElement()!
    }
}

这样,任何符合
caseitrable
的枚举都会自动执行该功能

在这种情况下,您不需要指定特定的值;它们将从
0
开始自动生成。仅当
enum
没有基于整数的原始类型时,才引用文档中的部分。在同一文档的下一部分:“当整数用于原始值时,如果没有为某些枚举成员指定值,它们将自动递增。”@SebastianFlückiger您需要从
arc4random
返回的原始值创建
geometryclassion
let shapeGeometry=geometryclassion(rawValue:(arc4random()%GeometryClassification.GeometryClassificationMax.rawValue))
@MikeS我不确定UInt32在这种情况下是否与“整数”相同。我假设是这样,但我认为,如果以这种方式使用枚举,则越具体越好。不过,我将编辑我的答案以反映这一点。@0O0O0它将自动生成带有
UInt32
的值。这些值也应该是正确的如果要使用从
arc4random
返回的值正常工作,请从
0
开始(因为它当前仍在使用).@SebastianFlückiger查看我上面的编辑-这应该是让它工作所需的全部。如果您仍然有问题,请告诉我。您是对的-我最终采用了这种方法以使其更具可读性。感谢您的努力。
++maxValue
将在Swift 3中被弃用。您将如何在代码中修复它?@Cesare:you c在
循环中移动增量。谢谢。关于如何使此代码可重用,您有什么想法吗?我的应用程序中有三个枚举,我使用此代码三次。我考虑创建一个类(因为您不能用扩展扩展扩展枚举?)。这在Swift 1.2中不起作用。它需要为枚举分配一个rawValue。反过来,这意味着它只适用于具有Int rawValue类型的枚举。我的代码有一个输入错误,返回了错误的类型。上的小写字母和缺少括号。allCases很重要。我无意中使用了
GeometryClassification.allCases().randomElement()!
(感谢您的自动完成),这是完全不同的,并导致运行时错误。对于Swift 5,这里有一个来自苹果的东西
extension CaseIterable {
    static func random<G: RandomNumberGenerator>(using generator: inout G) -> Self.AllCases.Element {
        return Self.allCases.randomElement(using: &generator)!
    }

    static func random() -> Self.AllCases.Element {
        var g = SystemRandomNumberGenerator()
        return Self.random(using: &g)
    }
}
let state = YourEnum.random()
extension CaseIterable {
    static func randomElement() -> AllCases.Element {
        guard Self.allCases.count > 0 else {
            fatalError("There must be at least one case in the enum")
        }
        return Self.allCases.randomElement()!
    }
}