【问题标题】:OCaml function that returns most frequent返回最频繁的 OCaml 函数
【发布时间】:2013-10-28 22:39:44
【问题描述】:

假设我有一个列表:

[1;3;4;2;1;5;1]

我需要编写一个函数,返回出现频率最高的数字,在这种情况下输出应该是

整数:1

有什么想法吗? 这是我到目前为止所拥有的,但它似乎并没有做任何事情,真的!

让 rec r ls = 匹配 ls

|[] -> 0

| hd::tl -> if(hd==(r tl)) then 1 + r tl else r tl;

【问题讨论】:

    标签: list recursion ocaml frequency


    【解决方案1】:

    您可以为每个数字在列表中出现的次数构建一个地图。这可以通过列表的单次遍历来构建。

    【讨论】:

    • 这听起来很愚蠢,但我将如何构建地图?我对这门语言非常陌生
    • 标准库中有一个Map 模块可供您使用。根本不是一个愚蠢的问题。
    • 请注意,这不适用于多态类型,因为 OCaml 的标准库没有带有多态键的映射。
    • @GordonGustafson 我不完全理解您的评论,但如果您想通用地编写函数,您可以请求一个比较函数作为额外参数。
    • @gasche 假设我想要一个函数val map_keys_to_zero : 'a list -> 'a comparator -> 'a Map,它返回'a list0 中每个元素的映射。可以这样做以使其与 any 'a 一起使用吗?我的简短研究表明并非如此,但作为 OCaml 的初学者,我很可能错了。 :)
    【解决方案2】:

    对列表进行排序。编写一个尾递归函数,其累加器包含:

    1. 那些比之前查看的元素更小的最常见的元素,或者最初是None
    2. 元素的计数 (1),或最初为 0
    3. 先前查看的元素,最初是排序列表的头部,
    4. 元素计数等于先前查看的元素,最初是1

    调用传递初始累加器和排序列表尾部的函数。

    【讨论】:

      【解决方案3】:

      基本实现 lukstafi 的答案(使用可变字段):

      type 'a accumulator = { mutable curr: 'a option; mutable cnt: int; 
        mutable el: 'a option; mutable max: int; }
      
      let rec process acc = function
        | [] -> acc.el
        | hd::tl -> 
          if Some(hd) = acc.curr then begin
            acc.cnt <- (acc.cnt + 1);
            if acc.cnt > acc.max then
              acc.max <- acc.cnt;
              acc.el <- Some(hd)
          end
          else begin
            acc.cnt <- 1;
            acc.curr <- Some hd
          end;
          process acc tl
      
      let option2string = function | None -> "" | Some v -> string_of_int v
      
      let () = 
        let sorted = List.sort compare [1;3;4;2;1;5;1] in
        let init = { curr = None; cnt = 0; el = None; max = 0 } in
        print_endline (option2string (process init sorted))
      

      【讨论】:

      • 感谢您的参考,但我更喜欢 gsg 的解释 :-)
      【解决方案4】:

      如果您将相等的元素与排序组合在一起并设置一个很好的循环不变量,这将非常简单。这个想法是扫描相同元素的运行,在每次运行结束时测试它是否是迄今为止最长的。

      让它变得简单的诀窍是进行预循环匹配以使边缘情况(一个空列表)脱离循环。

      let most_frequent_elt list =
        let rec loop maxelt maxcount elt count = function
          | [] -> if count > maxcount then elt else maxelt
          | x::xs ->
              if elt = x then loop maxelt maxcount elt (count + 1) xs
              else if count > maxcount then loop elt count x 1 xs
              else loop maxelt maxcount x 1 xs in
        match List.sort compare list with
         | [] -> None
         | x::xs -> Some (loop x 0 x 1 xs)
      

      【讨论】:

        【解决方案5】:

        答案较晚,但根据count unique elements in list in OCaml 此处的基准,您可以使用Hashtbls 获得良好的性能。

        然后,对元素及其出现次数进行简单排序,即可获得列表中最常见的元素。

        module IntHash =
          struct
            type t = int
                let equal i j = i=j
                let hash i = i land max_int
          end
        
        module IntHashtbl = Hashtbl.Make(IntHash)
        
        
        let count_unique_elements_int_hashtbl list =
          let counter = IntHashtbl.create 10000 in
          let update_counter x =
            if IntHashtbl.mem counter x then
              let current_count = IntHashtbl.find counter x in
              IntHashtbl.replace counter x (succ current_count)
            else
              IntHashtbl.replace counter x 1
          in
          List.iter update_counter list;
          IntHashtbl.to_seq counter
          |> List.of_seq
        
        
        let most_common_element_in_int_list list =
          count_unique_elements_int_hashtbl list
          |> List.sort (fun x y -> compare (snd x) (snd y)) 
          |> List.rev
          |> List.hd
          |> fst 
        
        let () =
          assert (most_common_element_in_int_list [1;2;1] = 1);
          assert (most_common_element_in_int_list [6;1;2;1;6;6] = 6);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-09-30
          • 2021-03-12
          • 1970-01-01
          • 1970-01-01
          • 2015-07-07
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多