Eine direkte Ausschneiden und Einfügen des folgenden Algorithmus:Merge Art, die aus „Programming Scala“ Stapelüberlauf verursacht
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))
}
}
verursacht eine Stackoverflow auf 5000 langen Listen.
Gibt es eine Möglichkeit, dies zu optimieren, so dass dies nicht auftritt?
Ich dachte über den Versuch, es tail rekursiv zu machen, sah dann eine ganze Menge von Informationen behaupten, dass die JVM ist nicht so zugänglich und nicht immer optimieren Tail-Rekursion. Gibt es eine Art Richtlinie für den Erfolg? – user44242
Die JVM nicht, also wird der Scala-Compiler es für Sie tun. Es tut nur unter bestimmten Voraussetzungen: es muss Selbstrekursion sein (dh f ruft g auf, und g, das f aufruft, wird nicht funktionieren), es muss natürlich eine _tail_ Rekursion sein (der rekursive Aufruf _muss_ muss immer das letzte sein) Code-Pfad), auf Methoden muss es entweder "final" oder "private" sein. Da 'merge' im Beispiel in' msort' definiert ist, ist es im Gegensatz zu einer Klasse oder einem Objekt effektiv privat. –
Ich denke, es wäre erwähnenswert hier, dass msort selbst ist nicht tail rekursiv, sondern merge ist. Für jeden, der nur vom Compiler überzeugt ist, fügen Sie der Definition von merge @ tailrec hinzu, und Sie werden feststellen, dass es als rekursive Funktion für den Tail akzeptiert wird, genau wie Daniel beschrieben hat. –