简体   繁体   English

如何实现 (string*int) list -> (string * int list) list 类型的 OCaml 函数,其中输出列表是输入项的计数

[英]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

The question I have is how might I transform a list of a string and integer pair to a list of string and int list pairs.我的问题是如何将字符串和整数对的列表转换为字符串和整数列表对的列表。

For example, if I have the list [("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) ; ("hi", 1) ; ("hello", 1) ; ("hi", 1) ; ("hey",1 )] then I should get back [("hello",[1;1]) ; ("hi", [1;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])] where basically from a previous function I wrote that creates string * int pairs in a list, I want to group every string that's the same into a pair that has a list of ones of a length = to how many times that exact string appeared in a pair from the input list. [("hello",[1;1]) ; ("hi", [1;1]) ; ("hey",[1])]基本上来自我之前编写的在列表中创建 string * int 对的函数,我想将每个相同的字符串分组为一对,该对具有长度 =到输入列表中该字符串在一对中出现的次数。 Sorry if my wording is confusing but I am quite lost on this function.对不起,如果我的措辞令人困惑,但我对这个功能完全迷失了。 Below is the code I have written so far:以下是我到目前为止编写的代码:

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])

Any help is appreciated!任何帮助表示赞赏!

General advice on how to improve understanding of core concepts:关于如何提高对核心概念的理解的一般建议:

The code suggests you could use more practice with destructuring and manipulating lists.该代码建议您可以在解构和操作列表方面进行更多练习。 I recommend reading the chapter on Lists and Patterns in Real World Ocaml and spending some time working through the first 20 or so 99 OCaml Problems .我建议阅读有关真实世界 Ocaml 中的列表和模式的章节,并花一些时间研究前 20 个左右的99 个 OCaml 问题

Some pointers on the code you've written so far:到目前为止您编写的代码的一些提示:

I have reorganized your code into a strictly equivalent function, with some annotations indicating problem areas:我已将您的代码重新组织为一个严格等效的函数,并附有一些说明问题区域的注释:

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 (_ :: _ :: _) *)

If you load your code in utop or compile it in a file, you should get a number of warnings and type errors that help indicate problem areas.如果您在utop加载您的代码或将其编译到一个文件中,您应该会收到许多有助于指出问题区域的警告和类型错误。 You can learn a lot by taking up each of those messages one by one and working out what they are indicating.您可以通过一条一条地处理这些消息并找出它们所指示的内容来学到很多东西。

Refactoring the problem重构问题

A solution to your problem using a fold over the input list is probably the right way to go.使用折叠输入列表来解决您的问题可能是正确的方法。 But writing solutions that use explicit recursion and break the task down into a number of sub-problems can often help study the problem and make the underlying mechanics very clear.但是编写使用显式递归并将任务分解为多个子问题的解决方案通常有助于研究问题并使底层机制非常清晰。

In general, a function of type 'a -> 'b can be understood as a problem:一般来说, 'a -> 'b类型的函数可以理解为一个问题:

Given a x : 'a , construct a y : 'b where ...给定一个x : 'a ,构造一个y : 'b其中 ...

Our function has type (string * int) list -> (string * int list) list and you state the problem quite clearly, but I've edited a bit to fit the format:我们的函数有类型(string * int) list -> (string * int list) list并且您非常清楚地说明了问题,但我已经编辑了一些以适应格式:

Given xs : (string * int) list , construct ys: (string * int list) list where I want to group every string from xs that's the same into a pair (string * int list) in ys that has a list of ones of a length = to how many times that exact string appeared in a pair from xs .给定xs : (string * int) list ,构造ys: (string * int list) list ,我想将来自xs的每个字符串分组到ys中的一对(string * int list)中,其中有一个列表a length = 到xs中该字符串在一对中出现的次数。

We can break this into two sub-problems:我们可以将其分解为两个子问题:

Given xs : (string * int) list , construct ys : (string * int) list list where each y : (string * int) list in ys is a group of the items in xs with the same string .给定xs : (string * int) list ,构建体ys : (string * int) list list ,其中每个y : (string * int) listys是一组中的项目的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

Given xs : (string * int) list list , construct ys : (string * int list) list where for each group (string, int) list in xs we have one (s : string, n : int list) in ys where s is the string determining the group and n is a list holding all the 1 s in the group.给定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

The solution to your initial problem will just be the composition of these two sub-problems:您最初的问题的解决方案将只是这两个子问题的组合:

let transform5 : (string * int) list -> (string * int list) list =
  fun xs -> (tally (group xs))

Hopefully this is a helpful illustration of one way to go about decomposing these kinds of problems.希望这是分解此类问题的一种方法的有用说明。 However, there are some obvious defects with the code I have written: it is inefficient, in that it creates an intermediate data structure and it must iterate through the first list repeatedly to form its groups, before finally tallying up the results.但是,我编写的代码有一些明显的缺陷:效率低下,因为它创建了一个中间数据结构,并且必须反复迭代第一个列表以形成其组,然后才能最终统计结果。 It also resorts to explicit recursion, whereas it would be preferable to use higher order functions to take care of iterating over the lists for us (as you tried in your example).它还采用显式递归,而最好使用高阶函数来为我们遍历列表(如您在示例中所尝试的那样)。 Trying to fix these defects might be instructive.尝试修复这些缺陷可能会有所启发。

Reconsidering our context重新考虑我们的背景

Is the problem you've posed in this SO question the best sub-problem from the overall task you are pursuing?您在这个 SO 问题中提出的问题是您所追求的整体任务中最好的子问题吗? Here are two questions have occurred to me:以下是我想到的两个问题:

Why, do you have a (string * int) list where the value of int is always 1 in the first place?为什么,您有一个(string * int) list ,其中int的值始终为1 Does this actually carry any more information than a string list ?这实际上比string list携带更多信息吗?

In general, we can represent any n : int by a int list which contains only 1 s and has length = n .通常,我们可以用一个仅包含1 s 且length = nint list来表示任何n : int By why not just use n here?为什么不在这里使用 n 呢?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM