Ocaml - 将列表的最后一个元素移到前面

时间:2011-03-22 21:01:46

标签: list recursion linked-list ocaml

首先,我很抱歉,如果这是一个令人困惑或倒退的方式来实现我想要完成的事情,但我是“Ocaml风格”的新手。

我想获取列表的最后一个元素,并将其移到列表的前面,将所有元素向上移动一个。

例如:拥有[1;2;3;4;5] -> [5;1;2;3;4]

据我所知,Ocaml中的列表基本上是链表,所以我计划递归遍历列表,找到最后一个元素,然后让该元素的尾部/剩余列表指向列表的头部。

我主要关注的是如何打破从倒数第二个元素到最后一个元素的链接。在上面的示例中,我希望将5指向1,但将4指向不再指向5。

我如何做到这一点,是否有更简单的方法来看待我完全失踪?

4 个答案:

答案 0 :(得分:7)

你不能“破解链接”,因为Ocaml列表是一个持久的数据结构。您无法真正修改列表,因此您必须按所需顺序生成包含值的新列表。

let thelist = [1;2;3;4;5] in
let lnewhead = List.hd (List.rev thelist) in
lnewhead :: (List.rev (List.tl (List.rev b)));;

你也可以在一个函数中定义它:

let flipper = fun thelist -> 
    (List.hd (List.rev thelist)) :: (List.rev (List.tl (List.rev thelist)));;

val flipper : 'a list -> 'a list = <fun>
# flipper([1;2;3;4;5]);;
- : int list = [5; 1; 2; 3; 4]

答案 1 :(得分:3)

通过确保List.rev thelist只计算一次,可以在时间复杂度方面略微改进Joshua的代码,如:

let flipper = fun thelist ->
   let r = List.rev thelist
   in (List.hd r) :: (List.rev (List.tl r))

答案 2 :(得分:1)

安全实施如下:

let rot1 l =
  let rec aux acc = function
      [] -> []
    | [x] -> x :: List.rev acc
    | x :: l -> aux (x :: acc) l
  in
  aux [] l

传递空列表返回空列表而不是引发异常,这是安全的。请注意,我强烈反对使用List.hd和List.tl,因为它们可能会失败,并带有一般错误消息。

此外,对aux的递归调用是一个尾调用(在返回之前要做的最后一件事)。 OCaml编译器将检测到这一点并避免在每次函数调用时增加堆栈(并可能引发异常或崩溃)。在处理长列表和递归函数时,需要注意这一点。

为了有效地执行此操作,即在O(1)而不是O(长度)中,您不能使用常规列表。您可以使用标准库中的队列模块或第三方提供的双向链接列表的实现。

以下是使用队列模块的示例:

 let rotate_queue q =
   if not (Queue.is_empty q) then
     let x = Queue.take q in
     Queue.add x q

 # let q = Queue.create ();;
 val q : '_a Queue.t = <abstr>
 # Queue.add 1 q;;
 - : unit = ()
 # Queue.add 2 q;;
 - : unit = ()
 # Queue.add 3 q;;
 - : unit = ()
 # Queue.iter print_int q;;
 123- : unit = ()
 # rotate_queue q;;
 - : unit = ()
 # Queue.iter print_int q;;
 231- : unit = ()
 # 

答案 3 :(得分:0)

电池库的Dllist module可能正是您要找的。这是一个必要的列表结构。