F# OCaml关于列表的简单练习

F# OCaml关于列表的简单练习,f#,ocaml,F#,Ocaml,各位早上好, 我必须做一个编程练习,但我被卡住了 这个练习需要一个函数,给定一个不为空的整数列表,返回出现次数最多的第一个数字 例如: 模式[1;2;5;1;2;3;4;5;5;4:5;5]=>5 模式[2;1;2;1;1;2]=>2 模式[-1;2;1;2;5;-1;5;5;2]=>2 模式[7]==>7 重要:练习必须在函数式编程中进行 我的想法是: let rec occurences_counter xs i = match xs with

各位早上好,

我必须做一个编程练习,但我被卡住了

这个练习需要一个函数,给定一个不为空的整数列表,返回出现次数最多的第一个数字

例如:

  • 模式[1;2;5;1;2;3;4;5;5;4:5;5]=>5
  • 模式[2;1;2;1;1;2]=>2
  • 模式[-1;2;1;2;5;-1;5;5;2]=>2
  • 模式[7]==>7
重要:练习必须在函数式编程中进行

我的想法是:

let rec occurences_counter xs i =  match xs with
                                |[] -> failwith "Error"
                                |x :: xs when x = i -> 1 + occurences_counter xs i
                                |x :: xs -> occurences_counter xs i;;
在这个函数中,我被卡住了:

提前感谢,我是编程和stackoverflow方面的新手
对不起,我的英语不好

一个解决方案是:首先计算一个夫妇列表(数量、发生次数)。 提示:使用List.assoc


然后,循环该对列表以查找最大出现次数,然后返回数字。

您需要处理输入列表,同时维护一个状态,该状态存储每个数字的出现次数。基本上,状态可以是一个
映射
,其中键位于列表元素的域中,值位于自然数的域中。如果您将使用
Map
,则算法将具有
O(NlogN)
复杂性。您还可以使用关联列表(即类型为
('key,'value)列表的列表
)来实现映射。这将导致二次复杂性。另一种方法是使用哈希表或长度等于输入域大小的数组。两者都会给你一个线性复杂度

在收集统计数据(即,从元素到其出现次数的映射)之后,您需要浏览获奖者的集合,并选择列表中的第一个

在OCaml中,解决方案如下所示:

open Core_kernel.Std

let mode xs : int =
  List.fold xs ~init:Int.Map.empty ~f:(fun stat x ->
      Map.change stat x (function
          | None -> Some 1
          | Some n -> Some (n+1))) |>
  Map.fold ~init:Int.Map.empty ~f:(fun ~key:x ~data:n modes ->
      Map.add_multi modes ~key:n ~data:x) |>
  Map.max_elt |> function
  | None  -> invalid_arg "mode: empty list"
  | Some (_,ms) -> List.find_exn xs ~f:(List.mem ms)
corebuild test.byte --
算法如下所示:

  • 运行输入并计算每个元素的频率
  • 运行统计数据并计算频谱(即从频率到元素的映射)
  • 获取具有最高频率的元素集,并在输入列表中找到一个元素,即此集合中的元素 例如,如果我们采取样本
    [1;2;5;1;2;3;4;5;5;4;5]

  • stats={1=>2;2=>2;3=>1;4=>2;5=>5}
  • mods={1=>[3];2=>[1;2];5=>[5]}
  • 您需要安装
    core
    库才能使用它。使用
    coretop
    在顶级中使用此功能。或者
    corebuild
    来编译它,如下所示:

    open Core_kernel.Std
    
    let mode xs : int =
      List.fold xs ~init:Int.Map.empty ~f:(fun stat x ->
          Map.change stat x (function
              | None -> Some 1
              | Some n -> Some (n+1))) |>
      Map.fold ~init:Int.Map.empty ~f:(fun ~key:x ~data:n modes ->
          Map.add_multi modes ~key:n ~data:x) |>
      Map.max_elt |> function
      | None  -> invalid_arg "mode: empty list"
      | Some (_,ms) -> List.find_exn xs ~f:(List.mem ms)
    
    corebuild test.byte --
    
    如果源代码存储在
    test.ml
    中,则建议:

    如果您之前对列表进行排序,您的算法可能会简化。这具有O(N log(N))复杂性。然后测量相同数字的最长序列


    这是一个很好的策略,因为您可以将工作的困难部分委托给一个众所周知的算法

    这可能不是最漂亮的代码,但下面是我提出的(F#)。首先,我将每个元素转换为中间格式。此格式包含元素本身、元素发生的位置和发生的数量

    tx型
    
    通过这个设置,我现在编写了两个附加函数。一个聚合函数,首先将每个元素转换为一个已发生的
    。T
    ,按
    x.element
    对它们进行分组(结果是一个列表)。然后它使用内部列表上的
    List.reduce
    ,将发生在同一元素上的事件添加到一起。结果是一个列表,该列表只包含单个
    已发生项。T
    ,每个元素都有第一个位置和已发生项的数量

    let聚合=
    List.mapi(乐趣x->occurrent.create x i 1)
    >>List.groupBy(有趣的occ->occ.Element)
    >>List.map(乐趣(x,occ)->List.reduce.add occ)
    
    现在可以使用该聚合函数实现不同的聚合逻辑。在您的情况下,您只需要出现次数最多、位置最低的一个。我编写了另一个函数来实现这一点

    让最早发生=
    List.sortWith(fun x y->(executed.compareOccurredPosition x y)*-1)>>List.head>>(fun x->x.Element)
    
    一个音符<代码>已发生。compareOccurredPosition编写为按升序对所有内容进行排序。我认为人们期望它在默认情况下从最小的元素到最大的元素。因此,默认情况下,第一个元素将是出现次数最少且位置最大的元素。通过将其结果与
    -1
    相乘,可以将该函数转换为降序排序函数。我这样做的原因是我可以使用
    List.head
    。我也可以使用
    List.last
    来获取最后一个元素,但我觉得最好不要为了获取最后一个元素而再次查看整个列表。最重要的是,您不希望出现
    。t
    您希望元素本身,因此我打开
    元素来获取数字

    一切都在行动

    让ll=[
    [1;2;5;1;2;3;4;5;5;4;5;5]
    [2;1;2;1;1;2]
    [-1;2;1;2;5;-1;5;5;2]
    [7]
    ]
    陆上通信线
    |>List.map聚合
    |>List.map最早出现
    |>List.iter(printfn“%d”)
    
    现在将打印此代码

    5
    2
    2
    7
    
    它还有一些粗糙的边缘,比如

  • 发生。如果尝试添加使用不同元素发生的事件,则添加将引发异常
  • List.head
    为空列表抛出异常

  • 在这两种情况下,都不会编写代码来处理这些情况或确保不会引发异常。

    您的代码中可能存在一个问题:x::mode l,因为mode l返回int而不是list。我想你应该把模式(x::l)。另一行上的修复相同。排序的可能重复不会给您带来任何好处。您仍然需要处理整个列表,并收集统计数据。事实上,它们被排序在这里是没有帮助的。排序将取消有关获取第一个数字的部分