[英]How to implement OCaml function of type (string*int) list -> (string * int list) list where the output list is a tally of the items in the input
我的问题是如何将字符串和整数对的列表转换为字符串和整数列表对的列表。
例如,如果我有列表[("hello",1) ; ("hi", 1) ; ("hello", 1) ; ("hi", 1) ; ("hey",1 )]
[("hello",1) ; ("hi", 1) ; ("hello", 1) ; ("hi", 1) ; ("hey",1 )]
[("hello",1) ; ("hi", 1) ; ("hello", 1) ; ("hi", 1) ; ("hey",1 )]
那么我应该回来[("hello",[1;1]) ; ("hi", [1;1]) ; ("hey",[1])]
[("hello",[1;1]) ; ("hi", [1;1]) ; ("hey",[1])]
[("hello",[1;1]) ; ("hi", [1;1]) ; ("hey",[1])]
基本上来自我之前编写的在列表中创建 string * int 对的函数,我想将每个相同的字符串分组为一对,该对具有长度 =到输入列表中该字符串在一对中出现的次数。 对不起,如果我的措辞令人困惑,但我对这个功能完全迷失了。 以下是我到目前为止编写的代码:
let transform5 (lst: (string *int) list) : (string *int list) list =
match lst with
| (hd,n)::(tl,n) -> let x,[o] = List.fold_left (fun (x,[o]) y -> if y = x then x,[o]@[1] else
(x,[o])::y,[o]) (hd,[o]) tl in (x,[1])::(tl,[1])
任何帮助表示赞赏!
该代码建议您可以在解构和操作列表方面进行更多练习。 我建议阅读有关真实世界 Ocaml 中的列表和模式的章节,并花一些时间研究前 20 个左右的99 个 OCaml 问题。
我已将您的代码重新组织为一个严格等效的函数,并附有一些说明问题区域的注释:
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
的每个字符串分组到ys
中的一对(string * int list)
中,其中有一个列表a length = 到xs
中该字符串在一对中出现的次数。
我们可以将其分解为两个子问题:
给定
xs : (string * int) list
,构建体ys : (string * int) list list
,其中每个y : (string * int) list
中ys
是一组中的项目的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
是包含组中所有1
的列表。
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
携带更多信息吗?
通常,我们可以用一个仅包含1
s 且length = n
的int list
来表示任何n : int
。 为什么不在这里使用 n 呢?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.