Note: in all this and the previous solutions, I've left out (most of the time) boundary conditions checks, to keep the code simple and as elegant as possible.
P23: Random select. We reuse P20 (removeAt) and remove a random element of the list recursively.
//P23
def randomSelect[A](n: Int, xs: List[A]): List[A] = (n, xs) match {
case (_, Nil) => Nil
case (0, _) => Nil
case (_, _) => {
val x = removeAt(new Random().nextInt(xs.size), xs)
x._2 :: randomSelect(n - 1, x._1)
}
}
P24: lotto. we create a list (well, actually is a range converted to a list) of the numbers and use P23 to select a random draw.
//P24
def lotto(n:Int,max:Int):List[Int] =randomSelect(n,(1 to max).toList)
P25: random permute. That's easy: we use P23 to do a randomSelect on the whole list
//P25
def randomPermute[A](xs:List[A]):List[A] =randomSelect(xs.size,xs)
P26: combinations. That one wasn't straightforward, I actually had to think! :)
But the solution can be defined recursively:
to get the combinations of size n, take one element of the set and append it to all the combinations of sets of size n-1 of the remaining elements, union the combinations of size n of the remaining elements.
I had to create an auxiliary function that "lift" a list into a list of lists, (kind of the opposite of flatten).
//P26
def combinations[A](n: Int, xs: List[A]): List[List[A]] = {
def lift[A](xs: List[A]): List[List[A]] = xs.foldLeft(List[List[A]]())((ys, y) => (List(y) :: ys))
(n, xs) match {
case (1, ys) => lift(ys)
case (i, xs) if (i == xs.size) => xs :: Nil
case (i, ys) => combinations(i - 1, ys.tail).map(zs => ys.head :: zs) ::: combinations(i, ys.tail)
}
}