Search code examples
listhaskellplaying-cards

Haskell function with deck of playing cards (returning value of hand)


i am in revision week and i am trying to work on a past paper for Haskell exam. However, this function is something i can't quite cope with.

The values of playing cards are represented like this: ’2’, ’3’, ’4’, ’5’, ’6’, ’7’, ’8’, ’9’, ’T’, ’J’, ’Q’, ’K’, ’A’. A hand of cards can be written as a String, for example “A563Q”.

I need to to write a function scoreHand :: [PlayingCardValue] -> Int that will return the total value held in a hand of cards. ’A’ has a value of 11. ’T’, ’J’, ’Q’ and ’K’ each have a value of 10. The other numbers have their face value (’2’ has a value of 2, ’3’ has a value of 3 and so on).

So i am supposed to write two versions of this function. First one using recursion and no library functions or list comprehension and second one the other way around (a.k.a) using list comprehension, library functions etc.

I wrote the version with recursion, but i am struggling with the other version.

Here is my code with recursion (even though i'm using one library function, but i will figure it out eventually)

*

import Data.Char
type PlayingCardValue = Char
scoreHand :: [PlayingCardValue] -> Int
scoreHand [] = 0
scoreHand (x:xs) =
  if x > '1' && x < '9'
    then digitToInt x + scoreHand (xs)
    else if x == 'T' || x == 'J' || x == 'Q' || x == 'K'
      then 10 + scoreHand (xs)
        else if x == 'A'
          then 11 + scoreHand (xs)
          else 0 + scoreHand (xs)

* Any ideas on how to create the same function not using recursion?


Solution

  • First of all I think you can make the code more elegantly, by introducing a new function:

    score :: PlayingCardValue -> Int
    score '2' = 2
    score '3' = 3
    score '4' = 4
    score '5' = 5
    score '6' = 6
    score '7' = 7
    score '8' = 8
    score '9' = 9
    score 'T' = 10
    score 'J' = 10
    score 'Q' = 10
    score 'K' = 10
    score 'A' = 11
    score _ = 0
    

    So now we can calculate the score of an individual card. We can use the digitToInt :: Char -> Int function if we want. It is also more clean how the score of a single card is calculated.

    Next we can use recursion with:

    scoreHand :: [PlayingCardValue] -> Int
    scoreHand [] = 0
    scoreHand (x:xs) = score x + scoreHand xs
    

    If we want to write a non-recursive one, we can make use of map :: (a -> b) -> [a] -> [b], and sum :: Num a => [a] -> a:

    scoreHand :: [PlayingCardValue] -> Int
    scoreHand xs = sum (map score xs)
    

    So by using map score xs, we create a new list, where for each card in the list xs, we have an element that contains the score, and then we sum up these values. We can write it more elegantly, by using function composition:

    scoreHand :: [PlayingCardValue] -> Int
    scoreHand = sum . map score