在Swift中循环枚举值

35

在 Swift 中是否可以循环枚举值?或者有什么替代方法吗?

我正在学习苹果的 Swift 语言指南,看到了这个关于枚举的示例。

//  EXPERIMENT
//
//  Add a method to Card that creates a full deck of cards, 
//  with one card of each combination of rank and suit.

struct Card {
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String {
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    }
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

enum Suit {
    case Spades, Hearts, Diamonds, Clubs
    func simpleDescription() -> String {
        switch self {
        case .Spades:
            return "spades"
        case .Hearts:
            return "hearts"
        case .Diamonds:
            return "diamonds"
        case .Clubs:
            return "clubs"
        }
    }
}

enum Rank: Int {
    case Ace = 1
    case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
    case Jack, Queen, King
    func simpleDescription() -> String {
        switch self {
        case .Ace:
            return "ace"
        case .Jack:
            return "jack"
        case .Queen:
            return "queen"
        case .King:
            return "king"
        default:
            return String(self.toRaw())
        }
    }
}

摘自:Apple Inc.《The Swift Programming Language》iBooks。https://itun.es/us/jEUH0.l

我尝试了以下方法,但文档说Swift中的枚举没有像C语言中那样分配基础整数值,所以我可能在错误的方向上努力了。

有更好的解决方法吗?

func deck() -> Card[]{
    var deck: Card[]
    for s in Suit {
        for r in Rank {
            deck += Card(rank: r, suit: s)
        }
    }
    return deck
}

func deck2() -> Card[]{
    var deck: Card[]
    for var s: Suit = .Spades; s <= .Clubs; s++ {
        for var r: Rank = .Ace; r <= .King; r++ {
            deck += Card(rank: r, suit: s)
        }
    }
    return deck
}

@MartinR 谢谢,我一开始搜索的时候没有找到这个。 不过有点不同,如果可能的话,我也在寻找不使用循环完成构建卡牌组的方法。也许我在帖子中应该更清楚地表达这一点。 - glaslong
1
嗯,看起来Swift没有一种特别简单的方法可以迭代枚举成员而不将它们定义为整数。有点令人失望,但我想我本来就不应该像这样使用枚举。当一个字典似乎是这种情况下更好的选择时,我仍然很好奇他们为什么在书中使用了它。 - glaslong
1个回答

23

还有其他方法吗?当然可以。它是否更好,这由您自行决定:

func generateDeck() -> Card[]
{
    let ranksPerSuit = 13
    var deck = Card[]()

    for index in 0..52
    {
        let suit = Suit.fromRaw(index / ranksPerSuit)
        let rank = Rank.fromRaw(index % ranksPerSuit + 1)

        let card = Card(rank: rank!, suit: suit!)
        deck.append(card)
    }

    return deck
}

let deck = generateDeck()

for card : Card in deck { println("\(card.description)") }
为了使用这个功能,您需要确保RankSuit枚举都使用Int作为它们的类型定义(例如:enum Rank:Int)。 Rank.Ace 应该等于 1,第一个 Suit case 应该等于 0
如果您想要类似于现有代码的循环,仍然应将枚举定义为Int类型,以便您可以使用例如Rank.King.toRaw()等方法。
苹果文档指出,枚举不限于“简单的整数值”,但如果您希望它们是这样,那肯定可以。 更新 灵感来自@jay-imerman的评论,适用于Swift 5
extension Rank: CaseIterable {}
extension Suit: CaseIterable {}

func generateDeck() -> [Card] {
    var deck = [Card]();

    Rank.allCases.forEach {
        let rank = $0

        Suit.allCases.forEach {
            let suit = $0

            deck.append(Card(rank: rank, suit: suit))
        }
    }

    return deck;
}

这是我见过的较好的答案之一。它简洁明了,没有试图放弃枚举来使用其他东西。谢谢! - glaslong
1
我不知道,我不太信服。我对此的问题在于它硬编码了迭代限制,而没有从枚举中推断出来。我希望能够在代码中获得枚举的限制/边界,并在for循环中使用它... - Jay Imerman
4
我在同一网站https://dev59.com/mmAg5IYBdhLWcg3wE3jQ上找到了一个非常优雅的答案,该答案是由sdduursma发布的。 - Jay Imerman
在最后一行,字符串表达式中的变量无法正常显示颜色。 :) - Amr Lotfy
你必须补偿索引中的0,所以设置->对于索引在0...51之间。 - uplearned.com
对于那些想知道的人,迭代将从“顶部”到“底部”进行。我的意思是,这听起来很明显,但为了更加精确~ - itMaxence

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接