Voici deux solutions pour l'exercice 4.9 Cay Horstmann de la Scala pour les Impatients: "Écrire une fonction lteqgt(valeurs: Array[Int], v: Int) renvoie une triple contenant le nombre de valeurs inférieures à v, égal à v, et plus grande que v." On utilise la récursivité tail, l'autre utilise une boucle while. Je pensais que les deux se compiler similaire bytecode, mais la boucle while est plus lente que la queue de la récursivité par un facteur de près de 2. Cela me fait penser que ma tandis que la méthode est mal écrit.
import scala.annotation.tailrec
import scala.util.Random
object PerformanceTest {
def main(args: Array[String]): Unit = {
val bigArray:Array[Int] = fillArray(new Array[Int](100000000))
println(time(lteqgt(bigArray, 25)))
println(time(lteqgt2(bigArray, 25)))
}
def time[T](block : => T):T = {
val start = System.nanoTime : Double
val result = block
val end = System.nanoTime : Double
println("Time = " + (end - start) / 1000000.0 + " millis")
result
}
@tailrec def fillArray(a:Array[Int], pos:Int=0):Array[Int] = {
if (pos == a.length)
a
else {
a(pos) = Random.nextInt(50)
fillArray(a, pos+1)
}
}
@tailrec def lteqgt(values: Array[Int], v:Int, lt:Int=0, eq:Int=0, gt:Int=0, pos:Int=0):(Int, Int, Int) = {
if (pos == values.length)
(lt, eq, gt)
else
lteqgt(values, v, lt + (if (values(pos) < v) 1 else 0), eq + (if (values(pos) == v) 1 else 0), gt + (if (values(pos) > v) 1 else 0), pos+1)
}
def lteqgt2(values:Array[Int], v:Int):(Int, Int, Int) = {
var lt = 0
var eq = 0
var gt = 0
var pos = 0
val limit = values.length
while (pos < limit) {
if (values(pos) > v)
gt += 1
else if (values(pos) < v)
lt += 1
else
eq += 1
pos += 1
}
(lt, eq, gt)
}
}
Ajuster la taille de bigArray en fonction de votre taille de segment de mémoire. Voici un exemple de sortie:
Time = 245.110899 millis
(50004367,2003090,47992543)
Time = 465.836894 millis
(50004367,2003090,47992543)
Pourquoi le tout en méthode beaucoup plus lent que la tailrec? Naïvement le tailrec version a l'air d'être à un léger désavantage, car il doit toujours effectuer 3 "si" vérifie pour chaque itération, alors que le tout en version souvent seulement d'effectuer 1 ou 2 tests en raison de la construction. (NB l'inversion de l'ordre-je effectuer les deux méthodes n'affecte pas le résultat).