好的,让我们分解你的代码。这是你的原件。
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
and 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被评估为副作用(think printf
),然后e1 的结果被丢弃。我认为你想要的是让e1accum
的结果成为递归调用的新值。因此,我们没有丢弃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'
; 对于以相反顺序排列的列表,素数是常规的。最后一个版本是我编译的唯一版本,我还没有测试任何代码。(我确实在我的另一个答案中测试了代码)。
我希望这个答案更有帮助。