1
votes

I'm trying to write a function "dom_rank r1 r2" that returns a boolean depending on the relative ranks of two playing Cards (rank is a type I defined, it has values "Six", "Seven", "Eight" ..... "King", "Ace"). If r1 is a King and r2 is a Jack, r1 is greater than r2 and the function returns true, and if r1 is Six and r2 is Ten then the function returns false. I understand that I could list out every possibility of two card inputs (I'm only dealing with cards Six-Ace, that makes about 30 different possibilities) but I'm trying to write simpler code. The method looks something like:

let dom_rank r1 r2 = match r1, r2 with
  | Ace, _ -> true
  | King, (Queen || Jack || Ten ........) -> true

I'm getting a syntax error at the beginning of the boolean expression "(Queen || Jack || Ten ........)". Why can't I list several possibilities for r2 in this way? Is there a way to write this expression in Ocaml?

2

2 Answers

5
votes

The "||" operator is a boolean operator, you must separate patterns with a simple "|", like this:

| King, (Queen | Jack | Ten | ... ) -> true

However, with this method, you'll still end up writing the word "Six" a lot of time. A good approach would be to use wildcards cleverly like that:

match r1, r2 with
| Ace, _ -> true
| _, Ace -> false (* at this point, neither card is an ace *)
| King, _ -> true
| _, King -> false
| Queen, _ -> true
| (* and so on *)

This way you only have two patterns per card rank, and no cardname as to be typed more than twice (and the non-cardname stuff can be copy-pasted)

A not so safe but convenient approach is to check that your type definition is in the right order:

type rank = Six | Seven | Eight | ... | Ace
let () = assert ( Six < Eight )

With this definition, the cards should be in the good order with respect to the standard comparison operators (see module Pervasives). Make sure to put some tests though, because any modification to the type may break your order.

1
votes

As you said yourself, || is a boolean operator. Queen, Jack etc. are not boolean values. Therefore you can not apply boolean operators to them (not to mention that you can't use arbitrary expressions as patterns in a match).

What you can do is to put multiple patterns next to each separated by | like this:

| King, Queen | King, Jack| King, Ten |... -> true

Of course that's not as succinct as you need to spell out King each time. You can avoid them by using nested pattern matches rather than matching on a tuple:

let dom_rank r1 r2 =
  match r1 with
  | Ace  -> true
  | King-> 
    (match r2 with
     | Ace -> false
     | _ -> true)
  | ...
  | Seven ->
    (match r2 with
     | Six, Five, Four, Three, Two -> true
     | _ -> false)

Another approach would be to define a function that maps each rank to an integer and then just define dom_rank as:

let dom_rank r1 r2 = int_of_rank r1 >= int_of_rank r2

That should cut down on a lot of repetition and is the best version in my opinion.