1

Starting my first project with Scala: a poker framework.

So I have the following class

class Card(rank1: CardRank, suit1: Suit){
val rank = rank1
val suit = suit1
}

And a Utils object which contains two methods that do almost the same thing: they count number of cards for each rank or suit

def getSuits(cards: List[Card]) = {

def getSuits(cards: List[Card], suits: Map[Suit, Int]): (Map[Suit, Int]) = {
  if (cards.isEmpty)
    return suits

  val suit = cards.head.suit
  val value = if (suits.contains(suit)) suits(suit) + 1 else 1
  getSuits(cards.tail, suits + (suit -> value))

}

getSuits(cards, Map[Suit, Int]())

}


def getRanks(cards: List[Card]): Map[CardRank, Int] = {

def getRanks(cards: List[Card], ranks: Map[CardRank, Int]): Map[CardRank, Int] = {
  if (cards isEmpty)
    return ranks

  val rank = cards.head.rank
  val value = if (ranks.contains(rank)) ranks(rank) + 1 else 1
  getRanks(cards.tail, ranks + (rank -> value))
}

getRanks(cards, Map[CardRank, Int]())
}

Is there any way I can "unify" these two methods in a single one with "field/method-as-parameter"?

Thanks

1 Answer 1

3

Yes, that would require high order function (that is, function that takes function as parameter) and type parameters/genericity

def groupAndCount[A,B](elements: List[A], toCount: A => B): Map[B, Int] = {
   // could be your implementation, just note key instead of suit/rank 
   // and change val suit = ... or val rank = ... 
   // to val key = toCount(card.head)
}

then

def getSuits(cards: List[Card]) = groupAndCount(cards, {c : Card => c.suit})
def getRanks(cards: List[Card]) = groupAndCount(cards, {c: Card => c.rank})

You do not need type parameter A, you could force the method to work only on Card, but that would be a pity.

For extra credit, you can use two parameter lists, and have

def groupAndCount[A,B](elements: List[A])(toCount: A => B): Map[B, Int] = ...

that is a little peculiarity of scala with type inference, if you do with two parameters lists, you will not need to type the card argument when defining the function :

def getSuits(cards: List[Card]) = groupAndCount(cards)(c => c.suit)

or just

def getSuits(cards: List[Card] = groupAndCount(cards)(_.suit)

Of course, the library can help you with the implementation

def groupAndCount[A,B](l: List[A])(toCount: A => B) : Map[A,B] =
   l.groupBy(toCount).map{case (k, elems) => (k, elems.length)}

although a hand made implementation might be marginally faster.

A minor note, Card should be declared a case class :

case class Card(rank: CardRank, suit: Suit) 
// declaration done, nothing else needed
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.