在OCaml中循环遍历数组的前n个元素

时间:2017-04-17 05:12:38

标签: arrays ocaml

我在OCaml中有以下函数,它将作为参数传递的数组中的第一个c元素相加:

let rec sum_array c a =
    if c < 0 then 0
    else a.(c) + sum_array (c - 1) a;;

我碰巧知道数组a是高级的,所以我想设置它。我试过了:

fun c -> let int_array = [| 1 ; 2 ; 3 ; 4 ; 5 |] in
    let rec sum_array c =
        if c < 0 then 0
        else int_array.(c) + sum_array (c - 1);;

但是OCaml抱怨模糊的“错误:语法错误”,这非常有帮助。

  1. 如何修复此功能定义?如何指定我希望返回sum_array的值?
  2. 如何让OCaml报告更多有用的错误消息?

3 个答案:

答案 0 :(得分:5)

在某种意义上,您的问题是您在错误的地方添加fun c -> ...

假设您有这样的功能:

let f count =
    (count + 72 - 1) / 72

如果你想象硬编码值72是你想要预先计算的东西,你可以按如下方式重写函数:

let f =
    let line_length = 72 in
    fun count -> (count + line_length - 1) / line_length

您的代码将数组放在函数体之前,但它应该位于let和新的内部函数定义之间。

您的情况特别棘手,因为您的函数是递归的。因此,您无法切换到fun c -> ...表单。相反,您可以在本地保留原始的基于let的定义。对于人为的例子,它看起来像这样:

let f =
    let line_length = 72 in
    let inner_f count =
        (count + line_length - 1) / line_length
    in
    inner_f

(作为旁注,你的代码总结了数组的第一个c + 1个元素,而不是第一个c元素。)

答案 1 :(得分:3)

错误消息显示在;;,因为只允许let in作为最外层语句。要评估表达式,您需要添加in sum_array c

fun c -> let int_array = [| 1 ; 2 ; 3 ; 4 ; 5 |] in
    let rec sum_array c =
        if c < 0 then 0
        else int_array.(c) + sum_array (c - 1)
    in sum_array c;;
- : int -> int = <fun>

这可以通过删除c上的抽象来简化:

let int_array = [| 1 ; 2 ; 3 ; 4 ; 5 |] in
    let rec sum_array c =
        if c < 0 then 0
        else int_array.(c) + sum_array (c - 1)
    in sum_array;;

在这两种情况下,sum_array都没有绑定在顶层。要么将其绑定到另一个let sum_array = ...,要在int_array的定义中提取sum_array的绑定(如@Jeffrey Scofield建议的那样),或绑定{{1}在定义int_array

之前的顶层
sum_array

答案 2 :(得分:1)

这个答案可能看起来有点冗长,但这仅仅是因为我从你的错误中看到某些基本的OCaml概念缺乏理解。这可能是一个清理一点的好机会。

OCaml中的范围和排序

您可能已经或可能尚未理解此主题。我仍然需要对此进行详细阐述,因为它们是以下内容的基础。

考虑一下如何测试代码:

let rec sum_array c a =
    if c < 0 then 0
    else a.(c) + sum_array (c - 1) a
in
sum_array 2 [| 1 ; 2 ; 3 ; 4 ; 5 |]         ;;

我假设你熟悉普通的编程语言。用Java方式表示:

//Java version
int a = 1;
int b = 1;
int c = a + b;

将被翻译成OCaml代码:

(* OCaml version *)
let a = 1 in
let b = 1 in
let c = a + b in
c       ;;

他们并没有那么不同。乍一看,OCaml代表测序的方式(即;)似乎很乏味。但随着你的函数式编程之旅的继续,你将更多地理解这种机制的重要性。

故障逻辑

结果显示:

let rec sum_array c a =
    if c < 0 then 0
    else a.(c) + sum_array (c - 1) a
in
sum_array 2 [| 1 ; 2 ; 3 ; 4 ; 5 |] ;;
(* - : int = 6   *)

你在这里做的逻辑有点不对劲。您的函数添加了一个不必要的元素。修复很简单:

let rec sum_array c a =
    if c = 0 then 0
    else a.(c - 1) + sum_array (c - 1) a
in
sum_array 2 [| 1 ; 2 ; 3 ; 4 ; 5 |] ;;
(* - : int = 3  *)

现在可行。该函数现在总结了数组的第一个n元素。

如何存储[| 1; 2; 3; 4; 5 |]进入函数,使它只接受参数n

我认为这就是你的意思:我碰巧事先了解了数组。

这里有一些简单的代码可以做很少的改变:

let rec sum_array c =
let a = [| 1 ; 2 ; 3 ; 4 ; 5 |] in
    if c = 0 then 0
    else a.(c - 1) + sum_array (c - 1)          ;;

然后你可以像这样运行它:

sum_array 3;;
(* - : int = 6   *)

请参阅此处的小改动只是使用另一个let ... in来对函数定义中a的定义进行硬编码。这正是人们在java中做同样事情的方式。

所有人都说,你自己的代码出了什么问题?

您的代码:

fun c -> let int_array = [| 1 ; 2 ; 3 ; 4 ; 5 |] in
    let rec sum_array c =
        if c < 0 then 0
        else int_array.(c) + sum_array (c - 1);;

失败有两个原因:

  • 阴影:看起来这个函数以c为参数,但实际上它在c传入之后无效。这就是&#39 ; s因为你的函数c(它是外部整体函数的辅助函数)有另一个同名参数sum_array,并且这个参数遮蔽了最外面的参数{{的绑定或定义。 1}}。
  • 范围:在此大函数的定义中,您定义了cint_array。但它们都是本地名称,你没有返回任何东西。回想一下,函数的返回值是箭头右侧的整个表达式的值。在这种情况下,这个价值是多少?除了一些局部变量的定义之外,什么都没有,一旦函数结束就会过期。

这是正确的代码:

sum_array

但还有另一个问题,上面这个函数是一个函数。这意味着它是一个价值。如果你现在没有将值绑定到变量,那么你现在就使用它,或者你没有跟踪它。所以在这里做的更好的事情是:

fun arg -> let int_array = [| 1 ; 2 ; 3 ; 4 ; 5 |] in
        let rec sum_array c =
            if c < 0 then 0
            else int_array.(c) + sum_array (c - 1)
        in sum_array arg

现在你可以像任何其他功能一样调用它。

当然,我仍在使用此代码中的错误逻辑。解决这个问题,我们有:

let sum_first_n = 
fun arg -> let int_array = [| 1 ; 2 ; 3 ; 4 ; 5 |] in
        let rec sum_array c =
            if c < 0 then 0
            else int_array.(c) + sum_array (c - 1)
        in sum_array arg        ;;

现在一切正常。

如何在功能上更好地做到这一点?

如果给出List的参数而不是数组,该怎么办?如果没有变异,你会怎么做?

这是帮助您更快地跟踪OCaml的好习惯。

以下是代码:

let sum_first_n = 
fun arg -> let int_array = [| 1 ; 2 ; 3 ; 4 ; 5 |] in
        let rec sum_array c =
            if c = 1 then int_array.(0)
            else int_array.(c - 1) + sum_array (c - 1)
        in sum_array arg        ;;

sum_first_n 3;;
(* - : int = 6        *)