In my opinion, this is a typical example of problem in which considering a more general case makes it easier. Consider the problem of finding all combinaisons of n
cards in a hand of l
cards.
For the sake of simplicity, I'll consider that card
s are just generic 'a
. Also, I won't make any difference between the hand and the table, I just consider both as a single list. Separating both could lead to better solutions, but make it less simple to understand :-)
So, you'll have a function comb n l
which returns a list of all the combinations of n
elements in the list l
(hence comb
returns a 'a list list
). Let's do it recursively:
First, if n
is greater than the length of l
, just give up:
exception NotEnoughElement of int*int
let rec comb n l =
let length_l = List.length l in
if n > length_l
raise (NotEnoughElement (n, length_l))
else
Now we are sure that n
is suitable, there are 3 cases to consider depending on n
: if n = 0
, then returns an empty list
match n with
| 0 -> []
if n
is the size of the list, then there is no choice: the only suitable sublist is... the list itself (this is similar in your case when you have card
s on the table). Notice that we return a list containing l
to fulfill our agreement that comb
returns a list of all sublist.
| _ when n == length_l -> [l]
finally, if n
is smaller than the length of l
, we have a choice to make: either we take the head of l
, either we don't.
| _ ->
If we take the head of l
, then we have to search for combination of n-1
elements in the tail of l
, and we append the head of l
to all those subcombinations. To do the appending, take this function
let app_head sublist = (List.hd l) :: sublist in
How to take the combination of n-1
elements in the tail of l
? Well, we have aggrement saying that comb n l
returns the list of all combination of size n
in l
, so we can use this "contract":
let sublists = comb (n-1) (List.tl l) in
Now, let's do the appending, the List module provides a map
function which applies a function to all elements of the list, we can use our app_head
function:
let combination_head_taken = List.map app_head sublists in
Now, if we don't take the head, then we still have to search for n
elements in the tail of l
. Once again, we use our aggrement on comb
:
let combination_head_not_taken = comb (n-1) (List.tl l) in
And then, just concatenate combinations generated in the two subcases:
combination_head_taken @ combination_head_not_taken
You can then play with utop (assuming you wrote this function in comb.ml
):
$ utop
# #use "comb.ml";;
# let hand = [1;2;3;4;5];;
# comb 3 hand;;
- : int list list = [[2; 3; 4; 5]; [1; 3; 4; 5]; [1; 2; 4; 5]; [1; 2; 3; 5]]