Une coupe directe et coller de l'algorithme suivant:tri fusion de « programmation Scala » provoque un débordement de pile
def msort[T](less: (T, T) => Boolean)
(xs: List[T]): List[T] = {
def merge(xs: List[T], ys: List[T]): List[T] =
(xs, ys) match {
case (Nil, _) => ys
case (_, Nil) => xs
case (x :: xs1, y :: ys1) =>
if (less(x, y)) x :: merge(xs1, ys)
else y :: merge(xs, ys1)
}
val n = xs.length/2
if (n == 0) xs
else {
val (ys, zs) = xs splitAt n
merge(msort(less)(ys), msort(less)(zs))
}
}
provoque une StackOverflowError sur 5000 listes longues.
Y a-t-il un moyen d'optimiser cela afin que cela ne se produise pas?
J'ai pensé à essayer de rendre la queue récursive, puis j'ai vu pas mal d'informations affirmant que la JVM n'est pas très compréhensible et n'optimise pas toujours la récursion de la queue. Y a-t-il une sorte de ligne directrice pour quand cela réussit? – user44242
La JVM ne fonctionne pas, donc le compilateur Scala le fera pour vous. Il ne le fait que sous certaines conditions: il doit être auto-récursif (c.-à-d. F appelant g, et g appelant f ne fonctionnera pas), il doit être _tail_ récursion, bien sûr (l'appel récursif _must_ toujours être la dernière chose sur ce chemin de code), sur les méthodes il doit être soit 'final' soit' private'. Dans l'exemple, parce que 'merge' est défini dans' msort', au lieu d'être défini sur une classe ou un objet, il est effectivement privé. –
Je pense qu'il peut être utile de mentionner ici que msort lui-même n'est pas récursif, mais la fusion est. Pour quiconque n'est convaincu que par le compilateur, ajoutez @tailrec à la définition de fusion, et vous remarquerez qu'il est accepté comme une fonction récursive de queue, comme l'a souligné Daniel. –