Bien, desglosemos su código. Aquí está tu original.
let rec meld3 l1 l2 accum =
if List.length l2 = 1 then
List.append accum [ (hd l2 + max (hd l1) (hd (tl l1)))]
else
(
List.append accum [ (hd l2 + max (hd l1) (hd (tl l1)))];
meld3 (tl l1) (tl l2) accum ;
)
La primera cosa que voy a hacer es volver a escribir por lo que un programador Caml lo entenderá, sin cambiar ninguno de los cálculos. Principalmente, esto significa el uso de patrones en lugar de hd
y tl
. Esta transformación es no trivial; es importante simplificar la manipulación de la lista para que sea más fácil identificar el problema con el código. También hace que sea más obvio que esta función falla si l2
está vacío.
let rec meld3 l1 l2 accum = match l1, l2 with
| x1::x2::xs, [y] -> (* here the length of l2 is exactly 1 *)
List.append accum [ y + max x1 x2 ]
| x1::x2::xs, y::ys -> (* here the length of l2 is at least 1 *)
(List.append accum [ y + max x1 x2 ]
; meld3 (x2::xs) ys accum
)
Ahora creo que la clave de su dificultad es la comprensión del operador de punto y coma. Si escribo ( E1; E2 ), la semántica es que e1 se evalúa de efecto secundario (piensa printf
) y luego el resultado de E1 se tira. Creo que lo que quiere en su lugar es que el resultado de e1 se convierta en el nuevo valor de accum
para la llamada recursiva.Así que en vez de tirar e1, hacemos un parámetro (este es el paso clave, donde el cálculo cambia en realidad):
let rec meld3 l1 l2 accum = match l1, l2 with
| x1::x2::xs, [y] -> (* here the length of l2 is exactly 1 *)
List.append accum [ y + max x1 x2 ]
| x1::x2::xs, y::ys -> (* here the length of l2 is at least 1 *)
(
meld3 (x2::xs) ys (List.append accum [ y + max x1 x2 ])
)
siguiente paso es observar que hemos violado el no hacer Repita usted mismo principio, y podemos arreglar eso por lo que el caso base, donde l2
está vacía:
let rec meld3 l1 l2 accum = match l1, l2 with
| x1::x2::xs, [] -> (* here the length of l2 is 0 *)
accum
| x1::x2::xs, y::ys -> (* here the length of l2 is at least 1 *)
(
meld3 (x2::xs) ys (List.append accum [ y + max x1 x2 ])
)
a continuación, limpiar un poco:
let rec meld3 l1 l2 accum = match l1, l2 with
| _, [] -> accum
| x1::x2::xs, y::ys -> meld3 (x2::xs) ys (List.append accum [ y + max x1 x2 ])
Finalmente, las llamadas repetidas a append
hacen que el código sea cuadrático. Este es un problema clásico con los parámetros que se acumulan y tiene una solución clásica: acumular la lista de respuestas en el orden inverso:
let rec meld3 l1 l2 accum' = match l1, l2 with
| _, [] -> List.rev accum'
| x1::x2::xs, y::ys -> meld3 (x2::xs) ys (y + max x1 x2 :: accum')
he cambiado el nombre accum
-accum'
; el primo es convencional para una lista en orden inverso. Esta última versión es la única versión que he compilado, y no he probado ninguno de los códigos. (Probé el código en mi otra respuesta).
Espero que esta respuesta sea más útil.
Esta es una vieja pregunta, pero mi consejo para cualquiera que trabaje en el problema mencionado, es representar el triángulo como una matriz de matrices. En Ocaml, las matrices son mutables, por lo que es fácil memorizar los subtotales en cada nivel ... –