Functional programming 在OCaml中的每个递归调用中保留一个计数器

Functional programming 在OCaml中的每个递归调用中保留一个计数器,functional-programming,ocaml,Functional Programming,Ocaml,我试图编写一个函数,返回给定列表中传递值v的索引x-如果未找到,则返回1。我的解决方案尝试: let rec index (x, v) = let i = 0 in match x with [] -> -1 | (curr::rest) -> if(curr == v) then i else

我试图编写一个函数,返回给定列表中传递值
v
的索引
x
-如果未找到,则返回1。我的解决方案尝试:

let rec index (x, v) =
    let i = 0 in
        match x with
        [] -> -1
        | (curr::rest) -> if(curr == v) then
                            i
                          else
                            succ i; (* i++ *)
                            index(rest, v)
;;

这对我来说显然是错误的(每次返回-1),因为它在每次传递时都会重新定义
i
。我有一些模糊的方法,在我的头脑中有单独的功能,没有一个我现在可以写下来。我知道这是所有编程中的一种常见模式,所以我的问题是,在OCaml中实现这一点的最佳方法是什么?

您不能在OCaml中变异变量(当然,有一种方法,但对于这样简单的事情,您确实不应该这样做)

您可以做的一个基本技巧是创建一个helper函数,该函数接收与要“变异”的变量相对应的额外参数。请注意,我是如何为
I
添加了一个额外的参数,并以类似的方式“修改”当前列表标题的

let rec index_helper (x, vs, i) =
    match vs with
      [] -> -1
      | (curr::rest) ->
          if(curr == x) then
              i
          else
              index_helper (x, rest, i+1)
;;

let index (x, vs) = index_helper (x, vs, 0) ;;
这种尾部递归转换是一种将循环转换为函数式编程的方法,但老实说,它的级别有点低(您拥有全部权限,但手动递归看起来像是使用gotos编程…)


对于某些特定的模式,您可以尝试利用可重用的高阶函数,例如map或fold。

这显然是一个家庭作业问题,所以我只做两个评论

首先,像
i
这样的值在OCaml中是不可变的。他们的价值观不会改变。因此,
succi
不会按照您的评论执行。它不会更改
i
的值。它只返回一个比
i
大一的值。它相当于
i+1
,而不是
i++


第二,递归的本质是想象如果你已经有了一个解决问题的函数,你将如何解决这个问题!唯一的诀窍是,只允许将问题的较小版本传递给另一个函数。在您的例子中,问题的较小版本是列表较短的版本。

在OCaml中,变异不是解决问题的常用方法。对于此任务,您应该在某些条件下使用递归并通过更改索引
i
来累积结果:

let index(x, v) =
    let rec loop x i =
        match x with
        | [] -> -1
        | h::t when h = v -> i
        | _::t -> loop t (i+1)
    in loop x 0
另一件事是使用
-1
作为例外情况不是一个好主意。你可能会忘记这个假设,把它当作其他指标来对待。在OCaml中,最好使用
选项
类型来处理此异常,这样编译器每次都会强制您处理

let index(x, v) =
    let rec loop x i =
        match x with
        | [] -> None
        | h::t when h = v -> Some i
        | _::t -> loop t (i+1)
    in loop x 0

是的,我认为
成功了是错误的。我是OCaml的新手,只是把它放在了那里。谢谢。是的,这就是我最后要做的,但我想知道是否有一个更简洁的语法,只包含一个内部函数?通常情况下,如果不需要尾部递归,有一个版本会更整洁一些。在这种情况下,您不需要helper函数。只需对用于比较的等式稍加注释:==是物理等式,这可能不是您想要的,例如字符串列表:
index(“foo”,“foo”)
返回-1。您可能更喜欢结构相等(=,即使它也不是没有问题的)。顺便说一句,我建议您使用选项类型(而不是特殊值),以使“未找到”案例更安全。在Haskell中,我们可能有,但我不知道它的Ocaml等价物的名称。正如missingno所写的:不要使用“-1”返回值——使用难以置信的类型系统,而不是将此错误隐藏为未使用的整数:而是写:“将x与|[]->None |…如果…那么(某些I)其他…”匹配为“其他”案例是未找到的。我不知道
选项
类型。注意到了。谢谢。为什么突变应该被视为坏习惯?“我不同意这一点。”托马斯:我想说,对于这种任务来说,变异是不自然的。将更新我的答案。@Thomas可变变量使程序更难理解,并且经常引入触发炸弹(只有当您以稍微不同的方式使用代码时,才会出现潜在的错误,导致可变变量在一个地方发生变异,而代码的其他部分则希望有自己的值)。它通常也比较慢,因为编译器的优化能力较差。根据我的经验,在局部使用时(即在函数体内部,可变单元不会导出到函数外部),变异不是问题。此外,这实际上取决于您使用的底层数据结构:在数组上使用递归而不是for循环对我来说没有任何意义。