OK, analizziamo il codice. Ecco il tuo originale.
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 prima cosa che ho intenzione di fare è riscrivere così un programmatore Caml lo capirà, senza cambiare nessuno dei calcoli. In primo luogo ciò significa utilizzare la corrispondenza del modello anziché hd
e tl
. Questa trasformazione è non banale; è importante semplificare la manipolazione delle liste per semplificare l'identificazione del problema con il codice. Rende anche più ovvio che questa funzione non riesce se l2
è vuoto.
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
)
Ora penso che la chiave della tua difficoltà sia la comprensione dell'operatore del punto e virgola. Se scrivo (e1; e2), la semantica è che e1 viene valutata per effetto collaterale (si pensi printf
) e poi il risultato di e1 viene gettato via. Penso che quello che vuoi invece sia per il risultato di e1 diventi il nuovo valore di accum
per la chiamata ricorsiva.Così, invece di buttare via e1, facciamo un parametro (questo è il passo chiave in cui il calcolo cambia in realtà):
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 ])
)
passo successivo è quello di osservare che abbiamo violato la Non ripetere Yourself linea di principio, e siamo in grado di risolvere che facendo il caso base dove l2
è vuota:
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 ])
)
abbiamo poi ripulire un po ':
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 ])
Infine, le chiamate ripetute a append
rendono il codice quadratico. Questo è un classico problema con i parametri accumulando e ha una soluzione classica: accumulare la lista risposta in ordine 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')
ho cambiato il nome accum
-accum'
; il primo è convenzionale per un elenco in ordine inverso. Quest'ultima versione è l'unica versione che ho compilato e non ho testato alcun codice. (Ho provato il codice nella mia altra risposta).
Spero che questa risposta sia più utile.
Questa è una vecchia domanda, ma il mio consiglio per chiunque lavori sul problema menzionato, è di rappresentare il triangolo come una matrice di array di ints. In Ocaml, gli array sono mutabili, quindi è facile memorizzare i subtotali a ogni livello ... –