【问题标题】:What is the correct and smooth way to write a OCaml function?编写 OCaml 函数的正确和流畅的方法是什么?
【发布时间】:2012-12-04 15:05:06
【问题描述】:

我正在学习Jason Hickey's Introduction to Objective Caml

在学习了第 3 章之后,我似乎了解了 letfun 的工作原理。但是,我仍然无法编写自己的fun


这是一个示例问题我面临的问题。

Write a function sum that, given two integer bounds n and m and a function f, computes a summation (no for loop allowed). i.e., sum n m f = f(n) + f(n+1) + ... + f(m)


那么,我应该如何开始考虑产生这个函数 sum?

在 Java 或普通编程语言中,这很容易。

由于这里不允许使用 for 循环,所以我想我应该以let rec 的方式进行操作?

类似这样的:

let rec sum n m f = fun i -> ....

我需要i 作为光标吗?

无论如何,我无法继续思考。

谁能为我指出一条产生 OCaml 乐趣的道路?


这是我的最终解决方案:

let rec sum n m f = if n <= m then (f n)+(sum n+1 m f) else 0;;

当然,这是错误的。错误是Error: This expression has type 'a -> ('a -> int) -> 'b but an expression was expected of type int

为什么?什么是'a?

【问题讨论】:

  • 仅供参考,如果你在 OCaml 中编程,你不应该再考虑循环:即使循环是 let rec 表达式的语法糖。
  • @KristopherMicinski 所以 OCaml 总是更喜欢 rec 吗?
  • 是的,这是执行任何类型递归的函数式方法。在 OCaml 的情况下,最好使用尾递归函数,它不会建立堆栈帧。永远不要再使用循环(除非你真的明白它们是语法糖,并且你有充分的理由更喜欢它们而不是递归)。
  • @KristopherMicinski 我的解决方案不是尾递归的,对吧?

标签: functional-programming ocaml


【解决方案1】:

我希望这将帮助您从递归而非循环的角度思考(让我们暂时忽略尾递归)。

所以你需要计算f(n) + f(n+1) + ... f(m)。它可能会帮助您以归纳的方式思考这个问题。也就是说,假设您知道如何计算f(n+1) + ... + f(m),那么您需要做什么才能计算出原始结果?好吧,您只需将f(n) 添加到后者,对吗?这正是您的代码所要表达的:

let rec sum n m f =
  if n = m then
    f m
  else
    f n + sum (n + 1) m f;; (* here's the inductive step *)

您可以看到我如何将f(n) 添加到f(n+1) + .... + f(m) 的结果中。因此,进行归纳思考,将问题分解为更小的部分,并思考如何将这些更小的部分的结果组合在一起。

希望我没有让事情变得更加混乱。

【讨论】:

  • let rec sum1 n m f = sum2 n m 0 and sum2 n m s = if n
  • 是的,这个想法是您使用 累加器 来跟踪到目前为止计算的总和。因此,您在进行计算时正在累积总和......所以最后您可以简单地返回累积值。这避免了对子问题返回的结果进行操作(即,您可以立即返回子问题的结果)。
【解决方案2】:

您犯了一个经典的语法错误:sum n+1 m f 被解析为 (sum n) + (1 n f) 而不是您所期望的。在 OCaml 中,函数应用(空格)的优先级高于中缀运算符。

类型错误来自sum n(您在总和中使用)不是整数的事实。它需要一个参数 (m) 和一个返回整数的函数。在类型推断过程的这一点上(发生错误时),OCaml 将其表示为'a -> ('a -> int) -> 'b:将一些未知的东西a,一个从a 转换为int 的函数,并返回一些东西b

【讨论】:

  • 是的,谢谢,我已将其更正为sum (n+1) m f,现在可以正常工作了。但是您能告诉我在制作fun 时如何思考吗?逻辑与其他简单的编程语言完全不同。
  • @JacksonTale 这样做并没有在评论中解释,这是通过成为一名函数式程序员并强迫自己完成一些示例来解释的。
【解决方案3】:

'a 就像 Java 中的泛型类型。例如: let test a = 1 它的类型是'a -> int 无论您的参数类型如何,此函数都会返回 1。

错误是你需要在这里放括号 (sum (n+1) m f)

Ocaml 将其视为额外的参数,因此它会产生与您预期不同的类型。加上括号可以确保你有正确数量的参数。当您有很多代码时,调试是一个微妙的问题。因此,在类似情况下使用括号可以为您节省大量时间。 :)

【讨论】:

    猜你喜欢
    • 2017-04-20
    • 2015-03-03
    • 1970-01-01
    • 1970-01-01
    • 2015-12-08
    • 2012-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多