关于如何提高对核心概念的理解的一般建议:
代码建议您可以多练习解构和操作列表。我建议阅读Real World Ocaml 中关于Lists and Patterns 的章节,并花一些时间阅读前20 个左右的99 OCaml Problems。
关于您目前编写的代码的一些提示:
我已将您的代码重新组织成一个严格等效的函数,并带有一些说明问题区域的注释:
let transform5 : (string * int) list -> (string * int list) list =
fun lst ->
let f (x, [o]) y =
if y = x then (* The two branches of this conditional are values of different types *)
(x, [o] @ [1]) (* : ('a * int list) *)
else
(x, [o]) :: (y, [o]) (* : ('a * int list) list *)
in
match lst with
| (hd, n) :: (tl, n) -> (* This will only match a list with two tuples *)
let x, [o] = List.fold_left f (hd, [o]) tl (* [o] can only match a singleton list *)
in (x, [1]) :: (tl, [1]) (* Doesn't use the value of o, so that info is lost*)
(* case analysis in match expressions should be exhaustive, but this omits
matches for, [], [_], and (_ :: _ :: _) *)
如果您在utop 中加载代码或将其编译到文件中,您应该会收到许多警告和类型错误,以帮助指出问题区域。通过逐条处理这些信息并弄清楚它们所表示的内容,您可以学到很多东西。
重构问题
使用折叠输入列表来解决您的问题可能是正确的方法。但是编写使用显式递归并将任务分解为多个子问题的解决方案通常有助于研究问题并使底层机制非常清晰。
一般'a -> 'b类型的函数可以理解为一个问题:
给定一个x : 'a,构造一个y : 'b,其中...
我们的函数类型为(string * int) list -> (string * int list) list,而你
很清楚地说明问题,但我已经编辑了一些以适应格式:
给定xs : (string * int) list,构造ys: (string * int list) list
我想将xs中的每个字符串组合成一对
(string * int list) 在ys 中有一个长度列表= 如何
很多次,确切的字符串从xs 中成对出现。
我们可以将其分解为两个子问题:
给定xs : (string * int) list,构造ys : (string * int) list list,其中ys中的每个y : (string * int) list是xs中具有相同string的一组项目。
let rec group : (string * int) list -> (string * int) list list = function
| [] -> []
| x :: xs ->
let (grouped, rest) = List.partition (fun y -> y = x) xs in
(x :: grouped) :: group rest
给定xs : (string * int) list list,构造ys : (string * int list) list,其中xs 中的每个组(string, int) list 在ys 中有一个(s : string, n : int list),其中s 是确定组的字符串,n 是一个列表持有群内所有1s。
let rec tally : (string * int) list list -> (string * int list) list = function
| [] -> []
| group :: xs ->
match group with
| [] -> tally xs (* This case shouldn't arise, but we match it to be complete *)
| (s, _) :: _ ->
let ones = List.map (fun (_, one) -> one) group in
(s, ones) :: tally xs
最初的问题的解决方案就是这两个子问题的组合:
let transform5 : (string * int) list -> (string * int list) list =
fun xs -> (tally (group xs))
希望这是对分解此类问题的一种方法的有用说明。但是,我编写的代码有一些明显的缺陷:它效率低下,因为它创建了一个中间数据结构,并且必须反复遍历第一个列表以形成其组,然后才能最终计算结果。它还采用显式递归,而最好使用高阶函数来为我们处理列表的迭代(正如您在示例中所尝试的那样)。尝试修复这些缺陷可能是有益的。
重新考虑我们的背景
您在这个 SO 问题中提出的问题是您所追求的整体任务中最好的子问题吗?我想到了两个问题:
为什么,你有一个(string * int) list,其中int 的值总是1?这实际上是否包含比string list 更多的信息?
一般来说,我们可以用一个int list 来表示任何n : int,它只包含1s 并且有length = n。为什么不在这里使用 n 呢?