Một cắt trực tiếp và dán của thuật toán sau đây:Merge sort từ "Lập trình Scala" gây ra stack overflow
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))
}
}
gây ra một StackOverflowError trên 5000 danh sách dài.
Có cách nào để tối ưu hóa điều này để điều này không xảy ra không?
Tôi nghĩ về việc cố gắng làm cho nó đuôi đệ quy, sau đó thấy khá nhiều thông tin tuyên bố rằng JVM không phải là amenable và không luôn luôn tối ưu hóa đệ quy đuôi. Có một số loại hướng dẫn cho khi điều này thành công? – user44242
JVM không, do đó trình biên dịch Scala sẽ làm điều đó cho bạn. Nó chỉ làm theo yêu cầu nhất định: nó phải là tự đệ quy (ví dụ, f gọi g, và g gọi f sẽ không hoạt động), nó phải là _tail_ đệ quy, tất nhiên (gọi đệ quy _must_ luôn là điều cuối cùng trên đó đường dẫn mã), trên các phương thức, nó phải là 'final' hoặc' private'. Trong ví dụ, vì 'merge' được định nghĩa bên trong' msort', thay vì được định nghĩa trên một lớp hoặc một đối tượng, nó có hiệu quả riêng tư. –
Tôi nghĩ rằng nó có thể là đáng nói đến ở đây rằng msort chính nó không phải là đệ quy đuôi, nhưng hợp nhất là. Đối với bất kỳ ai chỉ bị thuyết phục bởi trình biên dịch, thêm @tailrec vào định nghĩa hợp nhất, và bạn sẽ nhận thấy nó được chấp nhận như một hàm đệ quy đuôi, giống như Daniel đã vạch ra. –