向 Card 添加一个方法,该方法创建一副完整的纸牌,每个等级和花色组合一张牌

Posted

技术标签:

【中文标题】向 Card 添加一个方法,该方法创建一副完整的纸牌,每个等级和花色组合一张牌【英文标题】:Add a method to Card that creates a full deck of cards, with one card of each combination of rank and suit 【发布时间】:2014-06-08 18:56:01 【问题描述】:

所以我一直在做 Apple Swift Book 中的实验。

到目前为止,我已经能够做到所有这些,除了这个。以下是我尝试过的,但我不知道如何让它工作。

Card添加一个方法,该方法创建一副完整的纸牌,每个等级和花色组合都有一张牌。

// Playground - noun: a place where people can play

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())
        
    


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"
        
    


struct Card 
    var rank: Rank
    var suit: Suit

    func simpleDescription() -> String 
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    

    func createFullDeck() -> Array
        var FullDeck: Array

        FullDeck = Card(rank: .Ace, suit: .Spades)
        FullDeck = Card(rank: .Two, suit: .Spades)

        return FullDeck
    


let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

threeOfSpades.createFullDeck()
我不知道我应该为那个方法返回什么,一个数组? 我应该使用 for 循环来创建它吗?或者是否有适当/更简单的方法来使用枚举来做到这一点 为什么我要在 Card 中创建这个方法,调用 threeOfSpades.createFullDeck() 似乎不正确。

【问题讨论】:

【参考方案1】:

这是另一种方法,这次只使用您在那时已经学会的技术*

首先,我们使用之前定义的 RankSuit 枚举定义可能的等级和花色。

接下来我们让函数遍历每个花色中的每个等级,为每个花色创建一张卡片,最后返回一个卡片数组。

struct Card 
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String 
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    

    func createDeck() -> [Card] 
        let ranks = [Rank.ace, Rank.two, Rank.three, Rank.four, Rank.five, Rank.six, Rank.seven, Rank.eight, Rank.nine, Rank.ten, Rank.jack, Rank.queen, Rank.king]
        let suits = [Suit.spades, Suit.hearts, Suit.diamonds, Suit.clubs]
        var deck = [Card]()
        for suit in suits 
            for rank in ranks 
                deck.append(Card(rank: rank, suit: suit))
            
        
        return deck
    

(* 值得注意的例外是,游览没有明确解释当时如何附加到数组)

【讨论】:

您也可以使用+= 附加到数组(首先在“Generics”中提到大约第 20 页) 仅在附加另一个数组时 对于枚举枚举的可能值的更通用的方法,Nate Cook 在这里探索了一些更高级的技术:natecook.com/blog/2014/10/loopy-random-enum-ideas。当然,本书早期的 Swift 教程中不会涵盖这些内容。 不应该是func createDeck() -> [Card] 而不是func createDeck() -> Card[]。注意Card数组的语法 当您将createDeck 设为static func 并使用Card.createDeck() 调用它时,您只会得到 [(enum value), (enum value)] 52 次。你如何调用它并取回实际的字符串?【参考方案2】:

一个健壮的代码答案在生成牌组时不会使用枚举中的实际值(即 .Spades),例如,如果稍后将“Joker”添加到 Rank 枚举中(枚举中的任何位置),则牌组生成功能应该仍然可以正常工作。

设计问题(返回什么?,甲板生成是否应该是卡片的函​​数?)与本教程并不真正相关,但如果要实现任何重要的功能,Deck 类可能会更可取进一步构建(例如,随机播放)。所以现在,从 Card 结构中的函数返回一个数组就足够了。

以下代码(尽可能仅使用本教程中已描述的内容)在卡片结构中定义了一个函数,该函数循环遍历 Suit 和 Rank 枚举,而无需知道任何枚举值和返回一个数组:

static func deck() -> [Card] 
    var deck = [Card]()
    var suitCount = 1
    while let suit = Suit(rawValue: suitCount) 
        var rankCount = 1
        while let rank = Rank(rawValue: rankCount) 
            deck.append(Card(rank: rank, suit: suit))
            rankCount += 1
        
        suitCount += 1
    
    return deck

调用它:

let deck = Card.deck()
var card3 = deck[3].simpleDescription()

将函数复制到 Card 结构中并尝试向枚举添加值。请注意以下几点:

添加到枚举时循环的执行次数如何变化 两个枚举计数器都从 1 开始(如果枚举中没有另外指定,则第一个原始值为 1) 未指定的数组索引从 0 开始(例如,deck[3] 实际上是黑桃 4)

【讨论】:

我喜欢降低耦合的想法。但简单地在枚举中添加Joker 不会开箱即用 - 因为它会创建 4 个小丑,每个西装一个。小丑没有西装,所以无论哪种方式,你都必须为小丑设计特殊的箱子。 在操场上,enum Suit 没有原始值。要使此解决方案起作用,必须将枚举更改为使用一个:enum Suit: Int 【参考方案3】:

实验要求一种方法来卡片。所以我将方法声明为静态的,以便它作用于结构而不是它的实例:

struct Card 
    var rank: Rank
    var suit: Suit

    func simpleDescription() -> String 
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    

    static func deck() -> [Card] 
        var deck: [Card] = []
        for suit in [Suit.Spades, Suit.Hearts, Suit.Diamonds, Suit.Clubs] 
            for rank in 0...13 
                if let unwrappedRank = Rank.fromRaw(rank) 
                    deck.append(Card(rank: unwrappedRank, suit: suit))
                
            
        
        return deck
    

使用它:

let deck = Card.deck()

希望对您有所帮助。

【讨论】:

排名从 0...13 将是 14 步,你只需要 13 正确,条件 if let unwrappedRank = Rank.fromRaw(rank) 为 0 返回 nil,因此未附加甲板。 unwrappedRank 是可选的,因为 init?(rawValue:) 初始化程序是可选的。 Xcode 现在要求将Rank.fromRaw(rank) 写为Rank(rawValue: rank)【参考方案4】:

通过符合CaseIterable协议,您将免费获得allCases计算属性:

extension Rank: CaseIterable  
extension Suit: CaseIterable  

那么你可以使用maps 一次性实现:

func createDeck() -> [Card] 
    Suit.allCases.flatMap s in Rank.allCases.map r in Card(rank: r, suit: s) 

flatMap 用于将嵌套数组转换为一维(平面)数组。

【讨论】:

【参考方案5】:

for 循环是要走的路。我对您的基本代码进行了一些调整。首先,我向你的 Suit 枚举添加了一个类型。

enum Suit : Int

然后我添加了一个名为 Deck 的类,它负责一副纸牌。

class Deck 
    var cards:Card[]

    init() 
        self.cards = Array<Card>()
    

    func createDeck() 
        for suit in 0...Suit.Clubs.toRaw() 
            for rank in 1...Rank.King.toRaw() 
                self.cards += Card(rank: Rank.fromRaw(rank)!, suit: Suit.fromRaw(suit)!)
            
        
    

func createDeck() 循环遍历所有可能的扑克牌并将它们添加到您的牌组中。

【讨论】:

是的,这是有道理的......我想知道为什么苹果会说“实验:向 Card 添加一个方法来创建一副完整的卡片,每张卡片都有一张卡片军衔和花色的结合。”似乎上课会是一个更好的选择。 @downvoter 您能否解释一下您的决定,以便我做出更好的回答。 我不是反对者,但您不能再使用+= 添加元素,您必须使用.append() 方法或使用数组连接(即+= [element]),其中@ 987654327@运营商仍然支持。 PS - 我的回答也被神秘地否决了。【参考方案6】:

这是 Swift 3 的完整解决方案:

struct Card 

    var rank: Rank
    var suit: Suit

    func simpleDescription() -> String 
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    
    func createDeck() -> [Card] 
        let suits = [Suit.spades, Suit.hearts, Suit.clubs, Suit.diamonds]
        var deck = [Card]()

        for theSuit in suits 
            for theRank in Rank.Ace.rawValue...Rank.King.rawValue 
                deck.append(Card(rank: Rank(rawValue: theRank)!, suit: theSuit))
            
        
        return deck
    

你可以这样称呼它:

let aceOfHearts = Card(rank: .Ace, suit: .hearts)
let deck = aceOfHearts.createDeck()

【讨论】:

【参考方案7】:

我把所有东西都留在了 Swift Tour 中,Suit 是 String,Rank 是 Int。

struct Card 
    var rank: Rank
    var suit: Suit

    func simpleDescription () -> String
        return "The \(rank.simpleDescription()) of \suit.simpleDescription())"


    func createDeck() -> [Card] 
        var n = 1
        var deck = [Card]()
        let suits = [Suit.spades, Suit.hearts, Suit.diamonds, Suit.clubs]
        while let rank = Rank(rawValue: n) 
            for suit in suits 
                deck.append(Card(rank: rank, suit: suit))
            
            n += 1
        
        return deck
    
   
let card = Card (rank: Rank.ace, suit: Suit.spades)
let deck = card.createDeck()

【讨论】:

【参考方案8】:

您可以同时使用 zip 两个构建所有案例!

第一:

符合CaseIterable协议

extension Rank: CaseIterable  
extension Suit: CaseIterable  

所以您将免费获得allCases 计算属性:

Suit.allCases

Rank.allCases

第二:

那么你可以使用maps 一次性实现:

func createDeck() -> [Card] 
    zip(Rank.allCases, Suit.allCases).map  Card(rank: $0.0, suit: $0.1) 

zip 将构建需求,map 将全部转换为 Cards

【讨论】:

【参考方案9】:

首先我要解决一个最简单的问题:将创建完整套牌的代码放在哪里取决于你,但我建议你不要把它放在Card,而是创建一个Deck 类并提供一个convenience initializer 在那里进行。

也就是说,让我们继续将其添加到Card 类的计划。不幸的是,没有办法以您希望的方式循环遍历 Enum 的所有可能值(尽管我很想在这方面犯错!),但是您可以这样做:

let first_card = Rank.Ace.toRaw() // == 1
let last_card = Rank.King.toRaw() // == 13

for raw_rank in first_card...last_card 
    let rank = Rank.fromRaw(raw_rank)!

让我们来看看这个。枚举为每种情况分配一个基础值,并通过编写case Ace = 1 将其设置为从 1(而不是默认值 0)开始计数。 Enum 提供的用于访问底层值的 API 是每个 Enum 案例上的 toRaw() 方法(Enum 本身也以 Rank.toRaw(Rank.Ace) 的形式提供它。

您可以使用恰当命名的 fromRaw() 方法从原始值转换回来(所以 Rank.fromRaw(1) 会给我们 Ace),但有一个警告:它返回一个可选值。返回类型是Rank?不是 Rank。为了访问你需要either check for nil, or force unwrap it的值。

检查零:

if let rank = Rank.fromRaw(1) 
    // Do stuff with rank, which is now a plain old Rank

else 
    // handle nil

强制解包:

var rank: Rank = Rank.fromRaw(1)!

所以回答你关于循环的问题:是的,这就是这样做的方法 =P,关于数组也是如此,尽管这是一个设计决定。创建一个 Deck 类并返回它同样有意义。

让我们使用an extension 添加方法。扩展允许您向现有类型添加功能。您可以在类、枚举甚至原始类型上创建扩展。几乎任何东西。

extension Card 

    func createFullDeck() -> Card[] 
        var deck: Array<Card> = []
        for raw_rank in Rank.Ace.toRaw()...Rank.King.toRaw() 
            deck += [
                Card(rank:Rank.fromRaw(raw_rank)!, suit:.Spades),
                Card(rank:Rank.fromRaw(raw_rank)!, suit:.Hearts),
                Card(rank:Rank.fromRaw(raw_rank)!, suit:.Diamonds),
                Card(rank:Rank.fromRaw(raw_rank)!, suit:.Clubs),
            ]
        
        return deck
    


【讨论】:

是的,这是有道理的......我想知道为什么苹果会说“实验:向 Card 添加一个方法来创建一副完整的卡片,每张卡片都有一张卡片军衔和花色的结合。”似乎上课会是一个更好的选择。 @Arian 我同意,我认为他们只是想一次教一件事。初学者对以代码墙开头的示例非常敏感 即使您不打算创建 Deck 类型,createFullDeck 方法也应该是静态的。他们可能不会,因为此时他们还没有引入静态方法,但这更有意义。【参考方案10】:

我阅读了上面的答案,但后来我无法使用该方法......除非它是一个类方法。 所以我在我添加的 2 种方法之前添加了“静态”,这是我的建议:

struct Card 
  var rank: Rank
  var suit: Suit

  func simpleDescription() -> String 
    return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
  

  static func createDeck() -> Card[] 
    var deck = Card[]()
    for suit in [Suit.Spades, Suit.Clubs, Suit.Hearts, Suit.Diamonds] 
        for rankRawValue in 1...13 
            let rank = Rank.fromRaw(rankRawValue)
            let card = Card(rank: rank!, suit: suit)
            deck += card
        
    
    return deck
  

  static func printDeck(deck:Card[]) 
    for card in deck 
        println(card.simpleDescription())
    
  


let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()

let deck = Card.createDeck()
Card.printDeck(deck)

但我同意,“甲板”课程会是更好的选择......

【讨论】:

【参考方案11】:

令人惊讶的是,还没有人尝试过功能实现。如下:

extension Array 
  func flatten<T>() -> T[] 
    let xs = (self as Any) as Array<Array<T>>
    return xs.reduce(T[]())  (x, acc) in x + acc 
  


extension Card 
  static func fullDeck() -> Card[] 
    let rawRanks = Array(Rank.Ace.toRaw()...Rank.King.toRaw())
    let suits: Suit[] = [.Spades, .Hearts, .Diamonds, .Clubs]
    return (rawRanks.map 
      rawRank in suits.map 
        suit in Card(rank: Rank.fromRaw(rawRank)!, suit: suit)
        
      ).flatten()
  

【讨论】:

你有 swift 3 的版本吗?【参考方案12】:

试图避免了解枚举定义...看起来很笨拙(我是初学者),并且仍然需要起始索引:0 表示 Suit,1 表示 Rank。

struct Card 
    var rank: Rank
    var suit: Suit
    func simpleDescription() -> String 
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    
    static func deck() -> [Card] 
        var deck = [Card]()
        var suitCount = 0
        while (Suit(rawValue: suitCount) != nil) 
            var rankCount = 1
            while (Rank(rawValue: rankCount) != nil) 
                deck.append(Card(rank: Rank(rawValue: rankCount)!, suit: Suit(rawValue: suitCount)!))
                rankCount++
            
            suitCount++
        
        return deck
    

let deck = Card.deck()

【讨论】:

【参考方案13】:

我也刚开始学习 Swift,遇到了同样的问题。我也觉得这个实验是在 Card 结构中创建一个方法来创建一副完整的纸牌,这很奇怪。

在查看了这些答案并阅读了 Apple 官方“The Swift Programming Language (Swift 2.1)”教程后,我这样解决了:

struct Card 
    var rank: Rank
    var suit: Suit

    func simpleDescription() -> String 
        return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
    

    func createDeck() -> [Card] 
        let suits = [Suit.Spades, Suit.Hearts, Suit.Clubs, Suit.Diamonds]
        var deck = [Card]()

        for theSuit in suits 
            for theRank in Rank.Ace.rawValue...Rank.King.rawValue 
                deck.append(Card(rank: Rank(rawValue: theRank)!, suit: theSuit))
            
        

        return deck
    


let aceOfHearts = Card(rank: .Ace, suit: .Hearts)
let deck = aceOfHearts.createDeck()

for card in deck 
    print("\(card.rank) of \(card.suit)")

【讨论】:

我喜欢这个解决方案。我唯一担心的是你的实现知道Rank 实现的细节。但也许还可以。【参考方案14】:

由于上述所有示例本质上都是命令式的,并且 Swift 在构建时考虑到了函数式编程,因此我采用了更实用的方法来解决问题。这是我的全套代码:

我的排名枚举(必须定义一个包含所有值的数组,因为由于某种原因无法遍历枚举的所有值)

enum Rank: Int, CustomStringConvertible 

    case ace = 1
    case two, three, four, five, six, seven, eight, nine, ten
    case jack, queen, king

    static let allRanks = [ace, two, three, four, five, six, seven, eight, nine, ten, jack, queen, king]

    var description: String 
        switch self 
        case .ace:
            return "ace"
        case .jack:
            return "jack"
        case .queen:
            return "queen"
        case .king:
            return "king"
        default:
            return String(self.rawValue)
        
    

适合枚举(添加了类似类型的数组)

enum Suit: String, CustomStringConvertible  

    case spades = "♠︎"
    case hearts = "♥︎"
    case diamonds = "♦︎"
    case clubs = "♣︎"

    static let allSuits = [spades, hearts, diamonds, clubs]

    var description: String 
        switch self 
        default:
            return rawValue
        
    


...最后是卡片:

struct Card: CustomStringConvertible 
    var rank: Rank
    var suit: Suit

    var description: String 
        return "\(rank)\(suit)"
    

    static func createDeckOfCards() -> [Card] 
        return Suit.allSuits.reduce([]) 
            deck, suit in deck + Rank.allRanks.reduce([]) 
                cardsInSuit, rank in cardsInSuit + [Card(rank: rank, suit: suit)]
            
        
    


print(Card.createDeckOfCards())

【讨论】:

【参考方案15】:

作为一名 ios 开发人员,我尝试每年阅读这本书/教程一次。今年,我想我会作为一个新手开发人员来处理它,看看我能根据教程给出的信息做些什么。正如https://***.com/users/262455/jack-james 指出的那样,他们可能还没有教过.append。考虑到这一点,这是我的答案

func fullDeck() -> [String] 
    var deckOfCards = [String]()
    let suits = [Suit.clubs, Suit.diamonds, Suit.hearts, Suit.spades]
    let ranks = [Rank.ace, Rank.two, Rank.three, Rank.four, Rank.five, Rank.six, Rank.seven, Rank.eight, Rank.nine, Rank.ten ,Rank.jack, Rank.queen, Rank.king]
    for suit in suits 
        for rank in ranks 
            let card = Card(rank: rank, suit: suit)
            deckOfCards.append(card.simpleDescription())
        
    
    print(deckOfCards)
    return deckOfCards

我同意上面那个人的观点,一个类会更有意义,因为在这个例子中,你需要先初始化一个 Card 才能调用这个函数......

【讨论】:

【参考方案16】:
struct Card 
 let suit: Suit
 let rank: Rank

static func simpleDescription(suit: Suit, rank: Rank) -> String 
    let card = Card(suit: suit, rank: rank)
    return "The \(card.rank.simpleDescription) of \(card.suit.simpleDescription)"


static func createFullDeck() -> [Card]
    var fullDeck = [Card]()
    let ranks = Range(2...14)
    let suits = Range(0...3)

    ranks.map  (rank) in
        suits.map  (suit) in
            let card = Card(suit: Suit(rawValue: suit)!, rank: Rank(rawValue: rank)!)
            fullDeck.append(card)
        
    
    return fullDeck
 

用例

print(Card.simpleDescription(suit: .club, rank: .ace))

结果 = 俱乐部的王牌

Card.createFullDeck().map  (card) in
print(card.suit, card.rank)

result 打印卡片组的所有值。

【讨论】:

【参考方案17】:

Array 的map 方法允许在每个数组元素上调用闭包。 使用有效的 Swift 语法,我们可以用 map 调用替换显式的 for 循环:

func createDeck() -> [Card] 
        [Suit.spades, Suit.hearts, Suit.diamonds, Suit.clubs].flatMap  suit in
            Array(1...13).map  Card(rank: Rank(rawValue: $0)!, suit: suit) 
        
    

flatMap 用于将嵌套数组转换为一维(平面)数组。

$0 是一种速记 Swift 语法,用于通过编号访问闭包参数。这允许在内部闭包中跳过参数名称和 in 关键字。

【讨论】:

请务必解释您的代码,以便其他人能够理解。 添加了一些解释【参考方案18】:
static func deck() -> [Card] 
    var deck = [Card]()
    for rankRawValue in Rank.ace.rawValue...Rank.king.rawValue 
        deck.append(Card(rank: Rank(rawValue: rankRawValue)!, suit: Suit.spades))
        deck.append(Card(rank: Rank(rawValue: rankRawValue)!, suit: Suit.hearts))
        deck.append(Card(rank: Rank(rawValue: rankRawValue)!, suit: Suit.diamonds))
        deck.append(Card(rank: Rank(rawValue: rankRawValue)!, suit: Suit.clubs))
    
    return deck

已经提供了不止一个很好的答案,我喜欢 CaseIterable 解决方案。但这里只是另一种似乎可行的变体,我们将不胜感激同行评议。

【讨论】:

以上是关于向 Card 添加一个方法,该方法创建一副完整的纸牌,每个等级和花色组合一张牌的主要内容,如果未能解决你的问题,请参考以下文章

扑克牌练习 数据结构

有人可以帮我在pygame中为扑克游戏创建一副纸牌吗

在 Flutter 中动态创建一列行的好方法是啥

当我单击目标 C 中的一个按钮(添加按钮)时,如何创建一组按钮?

在轻松访问的同时创建一次 numpy 数组

创建一种方法来检查Java中的通用HashMap中是不是存在值? [复制]