【问题标题】:Ocaml noobie Q -- how to use accumulating parameters?Ocaml noobie Q——如何使用累加参数?
【发布时间】:2010-10-06 04:39:09
【问题描述】:

我正在尝试通过 Project Euler 的 Problem 18 来学习 Ocaml。我知道自己想做什么,只是不知道如何去做。

我有三个列表:

let list1 = [1;2;3;4;5];;
let list2 = [ 6;7;8;9];;
let line = [9999];;

我想将数字 list2 添加到 list1 中的最大相邻数字,IOW 我将添加 6+2、7+3、8+4 和 9+5 以获得列表 [8;10;12;14] .列表 line[] 是一个虚拟变量。

这是我的第三次尝试:

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 ;
    )
;;

let fu = meld3 list1 list2 line ;;

List.iter print_int fu;;

运行此程序后,我希望 line = [9999;8;10;12;14] 而不是 line = [9999]。 OTOH,fu 打印为 [999914]。

当我单步执行代码时,代码按预期执行,但没有任何变化; else 块中的 accum 永远不会被修改。

我就是不懂这种语言。谁能给点建议?

【问题讨论】:

  • 这是一个老问题,但我对解决上述问题的任何人的建议是将三角形表示为整数数组。在Ocaml中,数组是可变的,所以很容易记住每一层的小计...

标签: ocaml


【解决方案1】:

好的,让我们分解您的代码。这是您的原件。

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 程序员能够理解它,而不更改任何计算。这主要意味着使用模式匹配而不是hdtl。这种转变不是微不足道的;简化列表操作以更容易识别代码问题非常重要。如果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';对于以相反顺序排列的列表,素数是常规的。最后一个版本是我编译的唯一版本,我还没有测试任何代码。 (我确实在我的另一个答案中测试了代码)。

我希望这个答案更有帮助。

【讨论】:

  • 更有帮助,谢谢。我不知道;-语义。我之前尝试过匹配,但只有简单的示例可供参考。我知道其他问题(重复自己等);一旦我弄清楚发生了什么,就会开始修复它们。 :-)
【解决方案2】:

好吧,我认为您还没有掌握函数式编程的精髓:您需要将该值作为参数 accum 传递给递归调用,而不是调用 List.append 并将值丢弃。

我将通过将三角形几何与算术分离来解决这个问题。第一个函数采用两个列表(三角形的行)并生成一个新的三元组列表,每个包含和元素加上该元素的左右子元素。然后一个简单的映射生成一个列表,其中包含每个元素与其更大的子元素的总和:

(* function to merge a list l of length N with a list l' of length N+1,
   such that each element of the merged lists consists of a triple
     (l[i], l'[i], l'[i+1])
 *)

let rec merge_rows l l' = match l, l' with
  | [], [last] -> []   (* correct end of list *)
  | x::xs, y1::y2::ys -> (x, y1, y2) :: merge_rows xs (y2::ys)
  | _ -> raise (Failure "bad length in merge_rows")

let sum_max (cur, left, right) = cur + max left right

let merge_and_sum l l' = List.map sum_max (merge_rows l l')

let list1 = [1;2;3;4;5]
let list2 = [ 6;7;8;9]

let answer = merge_and_sum list2 list1

如果您正在研究 Euler 18,我建议您查找“动态规划”。

【讨论】:

  • (什么?我只能回复300字以内?!)我没有掌握函数式编程的基础;这就是我这样做的原因之一。你能解释一下为什么 'List.append accum [x]' 不将 x 附加到 accum 中吗?我相信你的方法行得通;我想知道为什么我的行不通
  • 简短的版本是它确实进行追加,但随后分号运算符将结果丢弃。我会发布一个更长的答案。
  • List.append accum [x] 返回一个新列表,而不是修改 accum。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-10-03
  • 2015-01-13
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多