Ocaml新手问——如何使用累加参数?
我正在尝试通过解决 Project Euler 的 问题 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 块中的累加值永远不会被修改。
我只是听不懂这种语言。 有人可以建议吗?
I'm trying to learn Ocaml by working on Problem 18 from Project Euler. I know what I want to do, I just can't figure out how to do it.
I've got three lists:
let list1 = [1;2;3;4;5];;
let list2 = [ 6;7;8;9];;
let line = [9999];;
I want to add the numbers list2 to the max adjacent number in list1, IOW I would add 6+2, 7+3, 8+4 and 9+5 to get a list [8;10;12;14]. The list line[] is a dummy variable.
Here's my third try:
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;;
After running this, I would expect line = [9999;8;10;12;14] but instead line = [9999].
OTOH, fu prints out as [999914].
When I step through the code, the code is executing as I expect, but nothing is changing; the accum in the else block is never modified.
I just don't get this language. Can anyone advise?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
好吧,我认为您还没有掌握函数式编程的本质:您需要将该值作为参数accum 传递,而不是调用List.append 并丢弃该值> 到递归调用。
我将通过将三角形几何与算术解耦来解决这个问题。 第一个函数采用两个列表(三角形的行)并生成一个新的三元组列表,每个三元组包含 和 元素以及该元素的左子元素和右子元素。 然后,一个简单的映射会生成一个列表,其中包含每个元素及其较大子元素的总和:
如果您正在研究 Euler 18,我建议您查找“动态规划”。
Well, I think you haven't grasped the essence of functional programming: instead of calling
List.append
and throwing the value away, you need to pass that value as the parameteraccum
to the recursive call.I would tackle this problem by decoupling the triangle geometry from the arithmetic. The first function takes two lists (rows of the triangle) and produces a new list of triples, each containing and element plus that element's left and right child. Then a simple map produces a list containing the sum of each element with its greater child:
If you are working on Euler 18, I advise you to look up "dynamic programming".
好的,让我们分解你的代码。 这是你的原件。
我要做的第一件事是重写它,以便 Caml 程序员能够理解它,而无需更改任何计算。 这主要意味着使用模式匹配而不是
hd
和tl
。 这种转变并非微不足道; 简化列表操作以便更容易地识别代码问题非常重要。 这也使得更明显的是,如果l2
为空,该函数就会失败。现在我认为你的困难的关键是对分号运算符的理解。 如果我写 (e1; e2),语义是 e1 被评估副作用(想想< code>printf),然后e1的结果被丢弃。 我认为您想要的是让 e1 的结果成为递归调用的
accum
的新值。 因此,我们没有丢弃e1,而是将其作为参数(这是计算实际发生变化的关键步骤):下一步是观察我们是否违反了不要重复自己的原则,我们可以通过使
l2
为空的基本情况来解决这个问题:然后我们进行一些清理:
最后,重复调用
append
使代码成为二次方。 这是一个典型的累加参数问题,并且有一个经典的解决方案:以相反的顺序累加答案列表:我已将名称
accum
更改为accum'
; 素数对于逆序列表来说是常规的。 最后一个版本是我编译的唯一版本,我还没有测试任何代码。 (我确实在其他答案中测试了代码)。我希望这个答案更有帮助。
OK, let's break down your code. Here's your original.
The first thing I'm going to do is rewrite it so a Caml programmer will understand it, without changing any of the computations. Primarily this means using pattern matching instead of
hd
andtl
. This transformation is not trivial; it's important to simplify the list manipulation to make it easier to identify the problem with the code. It also makes it more obvious that this function fails ifl2
is empty.Now I think the key to your difficulty is the understanding of the semicolon operator. If I write (e1; e2), the semantics is that e1 is evaluated for side effect (think
printf
) and then the result of e1 is thrown away. I think what you want instead is for the result of e1 to become the new value ofaccum
for the recursive call. So instead of throwing away e1, we make it a parameter (this is the key step where the computation actually changes):Next step is to observe that we've violated the Don't Repeat Yourself principle, and we can fix that by making the base case where
l2
is empty:We then clean up a bit:
Finally, the repeated calls to
append
make the code quadratic. This is a classic problem with accumulating parameters and has a classic solution: accumulate the answer list in reverse order:I've changed the name
accum
toaccum'
; the prime is conventional for a list in reverse order. This last version is the only version I have compiled, and I haven't tested any of the code. (I did test the code in my other answer).I hope this answer is more helpful.