我正在尝试编写一个函数,该函数返回给定列表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中执行此操作的最佳方法是什么?
答案 0 :(得分:7)
突变不是解决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中,最好使用option
类型处理此异常,以便编译器强制您每次都处理None
:
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
答案 1 :(得分:4)
这显然是一个家庭作业问题,所以我只会发两条评论。
首先,像i
这样的值在OCaml中是不可变的。他们的价值观没有改变。所以succ i
没有做你的评论所说的。它不会更改i
的值。它只返回一个大于i
的值。它相当于i + 1
,而不是i++
。
其次,递归的本质是想象如果你已经有一个解决问题的函数你将如何解决问题!唯一的诀窍是,您只能将此其他函数传递给较小的版本的问题。在您的情况下,问题的较小版本是列表较短的问题。
答案 2 :(得分:3)
你不能在OCaml中改变变量(嗯,有一种方法,但你真的不应该像这样简单的事情)
你可以做的一个基本技巧是创建一个辅助函数,它接收与你想要“变异”的变量相对应的额外参数。请注意我如何为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编程......)。
对于某些特定模式,您可以尝试做的是利用可重复使用的高阶函数,例如贴图或折叠。