好的,让我们分解您的代码。这是您的原件。
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 被评估 副作用(想想@ 987654326@) 然后 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';对于以相反顺序排列的列表,素数是常规的。最后一个版本是我编译的唯一版本,我还没有测试任何代码。 (我确实在我的另一个答案中测试了代码)。
我希望这个答案更有帮助。