I have tried my hands at "functionalising" a toy problem I had: find the expected number of throws of a six sided die until all sides have been seen (the answer is 14.7)
My starting point is my imperative
solution
import scala.collection.mutable.SortedSet
import scala.util.Random
def dice(nsim:Int) : Double = {
val nsides = 6
val r = Random
val throws = SortedSet[Int]()
var nthrows = 0
var res = 0.0
for (i <- 1 to nsim) {
throws.clear
nthrows = 0
while ( throws.size != nsides) {
nthrows += 1
throws += r.nextInt(nsides) + 1
}
res += nthrows
}
res / nsim
}
dice(10000) // ~14.7
I initialise an empty set, and keep adding my thrown dice until i have seen all sides (the set length equals six), while keeping track of how many die I have thrown. The expected number is just the sum of all these throws divided by the number of simulations (repetitions) I perform: approx 14.7
And here is my functional
attempt (almost half a day went into this, including plenty of googling, outofmemory
errors etc) - and I am not sure if this is considered good.
def throwdie(nsides:Int) : Int = {
val r = Random
r.nextInt(nsides) + 1
}
def nthrows(seen:SortedSet[Int], count:Int, nsides:Int) : Int = {
if (seen.size == nsides)
return count
return nthrows(seen + throwdie(nsides), count + 1, nsides)
}
def fdice(nsim:Int, nsides:Int) : Double = {
val runs = Iterator.fill(nsim)(SortedSet[Int]())
runs.map( x=> nthrows(x, 0, nsides) ).reduceLeft(_+_) * 1.0 / nsim
}
fdice(10000, 6) // ~14.7
explanation
So I start with a function throwdie
that just returns the outcome of rolling a single die.
Next I have tried to rewrite the while
loop using recursion. I came up with nthrows
which takes an empty set, a zero count, and the number of die sides, and returns the number of throws until all sides have been seen for one "simulation". I use a mutable data structure here, which as I understand is a nono in functional programming, but it is all I came up with.
Next I run the simulation in fdice
where I fill an Iterator
with nsim
instances of the empty SortedSet
, and apply nthrows
to all of these and sum the result.