OCaml:从列表中删除重复项,同时保持右侧的顺序

时间:2015-06-04 02:48:59

标签: ocaml

我刚刚阅读this thread并发现它很有趣。

我会在几分钟内实现remove from the left功能:

(*
 * remove duplicate from left:
 * 1 2 1 3 2 4 5 -> 1 2 3 4 5
 * *)
let rem_from_left lst =
  let rec is_member n mlst =
    match mlst with
    | [] -> false
    | h::tl ->
        begin
          if h=n then true
          else is_member n tl
        end
  in
  let rec loop lbuf rbuf =
    match rbuf with
    | [] -> lbuf
    | h::tl ->
        begin
          if is_member h lbuf then loop lbuf tl
          else loop (h::lbuf) rbuf
        end
  in
  List.rev (loop [] lst)

我知道我可以通过is_memberMap来实现hashtable以加快速度,但在这一刻我不关心。

在实施remove from the right的情况下,我可以List.rev实施:

(*
 * remove duplicate from right:
 * 1 2 1 3 2 4 5 -> 1 3 2 4 5
 * *)
let rem_from_right lst =
  List.rev (rem_from_left (List.rev lst))

我想知道我们是否可以用另一种方式实现它?

2 个答案:

答案 0 :(得分:4)

这是我实施remove_from_right的方式:

let uniq_cons x xs = if List.mem x xs then xs else x :: xs

let remove_from_right xs = List.fold_right uniq_cons xs []

同样,您可以按如下方式实施remove_from_left

let cons_uniq xs x = if List.mem x xs then xs else x :: xs

let remove_from_left xs = List.rev (List.fold_left cons_uniq [] xs)

两者都有其优点和缺点:

  1. 虽然List.fold_left是尾递归并且占用空间但它以相反的顺序折叠列表。因此,您需要List.rev结果。
  2. 虽然List.fold_right不需要跟List.rev,但它需要线性空间而不是常量空间才能产生结果。
  3. 希望有所帮助。

答案 1 :(得分:0)

您可以在返回的路上收集值,而不是在递归到最后的路上累积值:

let rem_from_right lst =
  let rec is_member n mlst =
    match mlst with
    | [] -> false
    | h::tl ->
        begin
          if h=n then true
          else is_member n tl
        end
  in
  let rec loop lbuf =
    match lbuf with
    | [] -> []
    | h::tl ->
        begin
        let rbuf = loop tl
        in
          if is_member h rbuf then rbuf
          else h::rbuf
        end
  in
  loop lst
相关问题