将列表一分为二并保留顺序

时间:2019-12-07 18:25:32

标签: ocaml

如何有效地将列表分成2个,以保留元素的顺序?

这是输入和预期输出的示例

[] should produce ([],[])
[1;] can produce ([1;], []) or ([], [1;])
[1;2;3;4;] should produce ([1; 2;], [3; 4;])
[1;2;3;4;5;] can produce ([1;2;3;], [4;5;]) or ([1;2;], [3;4;5;])

我尝试了几件事,但是我不确定哪个是最有效的……也许有一种解决方案使我完全错过了(对C代码的调用不计在内)。

我的第一个尝试是使用List的分区功能,将ref引用为列表长度的1/2。这种方法有效,但是当您只需要覆盖一半时,您就可以遍历整个列表。

let split_list2 l =
  let len = ref ((List.length l) / 2) in
  List.partition (fun _ -> if !len = 0 then false else (len := !len - 1; true)) l

我的下一个尝试是使用一个累加器,然后反转它。这只会遍历列表的一半,但我会反向调用以更正累加器的顺序。

let split_list4 l =
  let len = List.length l in
  let rec split_list4_aux ln acc lst =
    if ln < 1
    then
      (List.rev acc, lst)
    else
      match lst with
      | [] -> failwith "Invalid split"
      | hd::tl ->
        split_list4_aux (ln - 1) (hd::acc) tl in
  split_list4_aux (len / 2) [] l

我最后的尝试是使用累加器的函数闭包,并且它有效,但是我不知道闭包的效率如何。

let split_list3 l =
  let len = List.length l in
  let rec split_list3_aux ln func lst =
    if ln < 1
    then
      (func [], lst)
    else
      match lst with
      | hd::tl -> split_list3_aux (ln - 1) (fun t -> func (hd::t)) tl
      | _ -> failwith "Invalid split" in
  split_list3_aux (len / 2) (fun t -> t) l

那么,有没有一种最有效的方法来以OCaml(保留元素顺序)拆分列表?

2 个答案:

答案 0 :(得分:1)

您需要遍历所有解决方案的整个列表。 List.length函数遍历整个列表。但是,您以后的解决方案确实会重用原始列表的末尾,而不是构造新列表。

很难说仅仅通过检查就能确定给定的代码速度有多快。通常,最好以渐近的O(f(n))术语进行思考,然后通过时序测试(对实际数据)详细研究慢速函数。

您所有的答案看起来都是O(n),这是您可以做的最好的事情,因为您显然需要知道列表的长度才能得到答案。

您的split_list2split_list3解决方案对我来说看起来很复杂,因此,我会(直观地)认为它们会变慢。闭包是相当复杂的数据结构,其中包含函数和可访问变量的环境。因此,构建一个对象并不是那么快。

您的split_list4解决方案是我自己编写的代码。

如果您真的很在意计时,则应将解决方案安排在一些较长的清单上。请记住,在不同的系统上可能会获得不同的计时。

答案 1 :(得分:0)

不能放弃这个问题。我必须找到一种方法可以遍历此列表以创建保留顺序的拆分。.

怎么样?

let split lst =
  let cnt = ref 0 in
  let acc = ref ([], []) in
  let rec split_aux c l =
    match l with
    | [] -> cnt := (c / 2)
    | hd::tl ->
      (
        split_aux (c + 1) tl;
        let (f, s) = (!acc) in
        if c < (!cnt)
        then
          acc := ((hd::f), s)
        else
          acc := (f, hd::s)

      )
  in
  split_aux 0 lst; !acc