ОК, давайте разберем ваш код. Вот твой оригинал.
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 ;
)
Первое, что я собираюсь сделать, это переписать его, чтобы программист Caml понял это, без изменения каких-либо вычислений. В первую очередь это означает использование сопоставления с образцом вместо hd
и tl
. Это преобразование не тривиально; важно упростить манипулирование списком, чтобы упростить выявление проблемы с кодом. Это также делает более очевидным, что эта функция не работает, если l2
пусто.
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
)
Теперь я думаю, что ключом к вашему затруднению является понимание оператора точки с запятой. Если я напишу ( e1 ; e2 ), семантика такова, что e1 оценивается для побочного эффекта (думаю printf
) и затем результат e1 отбрасывается . Я думаю, что вместо этого вы хотите, чтобы результат e1 стал новым значением accum
для рекурсивного вызова. Поэтому вместо отбрасывания e1 мы делаем его параметром (, это ключевой шаг , где вычисление фактически изменяется):
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 ])
)
Следующим шагом является наблюдение, что мы нарушили принцип «Не повторяйся», и мы можем это исправить, сделав базовый случай, когда l2
пуст:
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 ])
)
Затем мы немного очистим:
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 ])
Наконец, повторные вызовы append
делают код квадратичным. Это классическая проблема с накоплением параметров и имеет классическое решение: накапливать список ответов в обратном порядке:
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')
Я изменил имя accum
на accum'
; штрих является обычным для списка в обратном порядке. Эта последняя версия является единственной версией, которую я скомпилировал, и я не тестировал ни один код. (Я проверял код в моем другом ответе).
Надеюсь, этот ответ более полезен.