向 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】:这是另一种方法,这次只使用您在那时已经学会的技术*
首先,我们使用之前定义的 Rank
和 Suit
枚举定义可能的等级和花色。
接下来我们让函数遍历每个花色中的每个等级,为每个花色创建一张卡片,最后返回一个卡片数组。
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
那么你可以使用map
s 一次性实现:
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
第二:
那么你可以使用map
s 一次性实现:
func createDeck() -> [Card]
zip(Rank.allCases, Suit.allCases).map Card(rank: $0.0, suit: $0.1)
zip
将构建需求,map
将全部转换为 Card
s
【讨论】:
【参考方案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 添加一个方法,该方法创建一副完整的纸牌,每个等级和花色组合一张牌的主要内容,如果未能解决你的问题,请参考以下文章