So I have been doing the experiments that are in the Apple Swift Book.
I have been able to do all of them, except for this one so far. Below is what I have tried, but I can't figure out how to get it working.
Add a method to Card that creates a full deck of cards, with one card of each combination of rank and suit.
// 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()
- I don't know what I am supposed to return for that method, an Array?
- Should I use a for loop to create this? or is there a proper/easier way to do this with enum
- Why would I create this method inside of Card, calling
threeOfSpades.createFullDeck()
seems incorrect.
Here's another way of doing it, this time only using techniques you would have learned up to that point*
First we define the possible ranks and suits, using the respective Rank
and Suit
enums defined previously.
Next we have the function iterate over each rank within each suit, creating a card for each, and finally returning an array of the cards.
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
}
}
(* with the notable exception that the tour hadn't explicitly explained how to append to arrays at that point)
A robust code answer will not to the use actual values (i.e., .Spades) from the enumerations when generating the deck, e.g., if a "Joker" is added later to the Rank enumeration (anywhere in the enumeration), the deck generation function should still work without change.
The design questions (what to return?, should the deck generation be a function of card?) are not really relevant to this tutorial, but it is likely that a Deck class would be preferable if any serious functionality is going to be built out further (e.g., shuffle). So for now, returning an Array from a function in the Card structure is all that is required.
The following code (as far as possible only using what has been described up to this point in the tutorial) defines a function in the Card Structure that loops through the Suit and Rank enumerations without needing to know any of the enumeration values and returns an Array:
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
}
Invoke this with:
let deck = Card.deck()
var card3 = deck[3].simpleDescription()
Copy the function into the Card structure and try adding values to enums. Note the following:
- how the number of times the loops are executed changes when adding to the enums
- that both enum counters start at 1 (if not otherwise specified in the enumeration the first raw value is one)
- unspecified array indexes start at 0 (e.g., deck[3] is actually the 4 of Spades)
The experiment asks for a method to Card. So I I declared the method as being static so that it acts on the struct and not an instance of it:
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
}
}
To make use of it:
let deck = Card.deck()
Hope that helps.
A for loop is the way to go. I made a few adjustments in your base code. First, I added a type to your Suit enum.
enum Suit : Int
Then I added a class called Deck which is responsible for a deck of cards.
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()
loops through all possible playing cards and adds them to your deck.
I left everything like in Swift Tour, Suit is String and Rank is 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()
First I'll tackle the easiest question: Where you put the code that creates the full deck is up to you, but I would advise you not to put it in Card
, but rather create a Deck
class and provide a convenience initializer to do it there.
That said, let's continue with the plan of adding it to the Card
class. Unfortunately there is no way to just loop over all possible values of an Enum in the way you'd hope (though I'd love to be wrong about this!), but you can do this:
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)!
}
Let's walk through this. An Enum assigns an underlying value to each case, and by writing case Ace = 1
you're setting it up to start counting from 1 (rather than 0, the default). The API provided by an Enum for accessing the underlying value is a toRaw()
method on each Enum case (The Enum itself also provides it in the form of Rank.toRaw(Rank.Ace)
.
You can convert back from the raw value using the aptly named fromRaw()
method (so Rank.fromRaw(1)
would give us Ace) but there is a caveat: It returns an optional. The return type is Rank?
, not Rank
. In order to access the value you need to either check for nil, or force unwrap it.
Checking for nil:
if let rank = Rank.fromRaw(1) {
// Do stuff with rank, which is now a plain old Rank
}
else {
// handle nil
}
Force unwrap:
var rank: Rank = Rank.fromRaw(1)!
So to answer your question about the loops: Yes, that's the way to do it =P, and yes again about the array, though that's a design decision. It makes just as much sense to create a Deck
class and return that instead.
Let's add the method using an extension. Extensions let you add functionality to an existing type. You can create an extension on a class, enum, or even a primitive type. pretty much anything.
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
}
}
I read the answers above, but then I couldn't use the method ... unless it is a class method. So I added "static" before the 2 methods I added, and here is my proposal:
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)
But I agree, a "Deck" class would be a better option ...
Surprisingly, no one has yet had a stab at a functional implementation. Here goes:
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()
}
}
Trying to avoid knowledge of the enum definition... It seems clumsy (I'm a beginner), and still needs the starting index: 0 for Suit, 1 for 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()
I just started learning Swift also, and had this same problem. I too thought it was rather odd that the experiment was to create a method inside the Card structure to create a full deck of cards.
After looking at these answers, and reading the official Apple "The Swift Programming Language (Swift 2.1)" tour, I solved it like this:
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)")
}
Here's the whole solution for 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
}
}
You can call it like this:
let aceOfHearts = Card(rank: .Ace, suit: .hearts)
let deck = aceOfHearts.createDeck()
Since all the examples above are imperative in nature and Swift is built with functional programming in mind, I took a more functional approach to solving the problem. Here's my full set of code:
My Rank enum (have to define an array with all the values because it's not possible to iterate over all the values of an enum for some reason)
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)
}
}
}
Suit enum (added similar type of array)
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
}
}
}
...and finally the card:
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())
As an iOS dev I try to read this book/tutorial about once a year. This year, I thought I'd approach it as a novice developer, and see what I could do based on what information the tutorial had given up to this point. As https://stackoverflow.com/users/262455/jack-james noted they may not have taught .append yet. With that in mind, here is my answer
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
}
I agree with the guy above, a class would make more sense, because in this example you need to initialize a Card first in order to call this function...
来源:https://stackoverflow.com/questions/24109691/add-a-method-to-card-that-creates-a-full-deck-of-cards-with-one-card-of-each-co