Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct (functional) way to alter a list and return what was removed?

Tags:

scala

I'm trying to learn scala, and decided to create a poker app to get my head around some of the class objects. I've got decks working fine, but I've got to the point where I need to draw 5 cards. So far I have:

import util.Random

case class Card(value: Int, color: String)

class Deck {
  private var deck = newDeck 

  def draw(amount: Int): List[Card] = {
    val ret = deck.take(amount)
    deck = deck.drop(amount)
    ret
  }

  def newDeck: List[Card] = {
    Random.shuffle((1 to 13).map(x => 
      List(Card(x, "D"), Card(x, "C"), Card(x, "H"), Card(x, "S"))).toList.flatten)
  }

  override def toString: String =  "Deck has " + deck.length + " cards left."
}

This draw function doesn't seem quite right having two steps - but I'm not sure how I else I can (or should) take the top however many cards, and leave the list in a state without those cards in?

(As an aside, if someone has a better function for the deck creation/shuffle I'm all ears, this seems a bit hacky too... but my main question is the list state)

like image 678
CaffeinatedDave Avatar asked Mar 22 '23 06:03

CaffeinatedDave


1 Answers

In my opinion, you should rewrite the codes either:

(a) with totally immutable struct, i.e. NO var, NO mutable collection

OR

(b) replace var deck: List[Card] by some mutable collection, like ListBuffer.

Here is the (a) solution:

import util.Random

case class Card(value: Int, color: String)

class Deck(private val cards: Seq[Card]) {
  def draw(amount: Int): (Deck, Seq[Card]) = {
    val (ret, rem) = cards.splitAt(amount)
    (new Deck(rem), ret)
  }

  override def toString: String = "Deck has " + cards.size + " cards left."
}

object Deck {
  def apply(cards: Seq[Card] = Nil): Deck = cards match {
    case Nil =>
        val ncds = for(v <- 1 to 13; c <- Seq("D", "C", "H", "S")) yield Card(v, c)
        new Deck(Random.shuffle(ncds))
    case _ => new Deck(cards)
  }
} 

Use case:

scala> :paste
// Entering paste mode (ctrl-D to finish)
//paste code here

// Exiting paste mode, now interpreting.

import util.Random
defined class Card
defined class Deck
defined object Deck

scala> val d1 = Deck()
d1: Deck = Deck has 52 cards left.

scala> val (d2, cards) = d1.draw(4)
d2: Deck = Deck has 48 cards left.
cards: Seq[Card] = Vector(Card(3,H), Card(2,S), Card(11,H), Card(8,C))
like image 163
Eastsun Avatar answered Mar 24 '23 19:03

Eastsun