从列表中删除最后一个元素的最简单方法

时间:2016-09-29 22:48:54

标签: f#

我正在编写一个将采用整数列表的函数,例如:

let x = [1..5]

并且应该返回如下所示的洗牌列表:

[1;5;2;4;3]

所以,它应该采用第一个元素然后是最后一个,然后是第二个元素,然后是最后一个,依此类推...

到目前为止我写过这个函数:

let rec reshuffle list: List<int> =
  match list with
  | [] -> []
  | head :: tail -> [head;list |> List.last] @reshuffle tail

我得到了这个答案:

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

似乎这是因为每次递归完成函数时,都不会删除最后一个元素。如何删除函数中的头部和尾部?

8 个答案:

答案 0 :(得分:3)

如果我们假设没有允许库函数并试图避免一次又一次地处理列表“没有”,我建议这样做。

我们首先反转初始列表,只执行一次 我们遍历这些列表并停止一半,从两个列表中累积当前项目。 起始累加器为空或包含“中间”项,具体取决于初始列表的长度是奇数/偶数。

为此我们需要一个函数来反转列表:

let reverse xs =
  let rec aux acc = function
    | []      -> acc
    | x :: xs -> aux (x :: acc) xs
  aux [] xs

然后我们需要一个函数来获取列表的长度:

// not tail-rec
let rec length = function
  | []      -> 0
  | _ :: xs -> 1 + length xs

最后我们可以编写所需的函数:

let reshuffle xs =
  let reversed = reverse xs

  let rec aux acc = function
    | -1    -> acc
    | index -> let newAcc = xs.[index] :: reversed.[index] :: acc
               aux newAcc (index - 1)

  let theLength = length xs
  let startAcc = if theLength % 2 = 0
                 then []
                 else [ xs.[theLength / 2] ]

  aux startAcc (theLength / 2 - 1)

或者,如果您认为某些索引的递归感觉某种“作弊”并且递归应该出现在列表中,您可以使用它: (较短但会做“双重”工作,一半为“无”)

let reshuffle xs =
  let rec aux acc = function
    | x :: xs, r :: rs -> aux (r :: x :: acc) (xs, rs)
    | _                -> acc

  let full = aux [] (xs, reverse xs)
  full.[.. length xs - 1]

我们构建了“对”的完整列表,因此我们得到一个从初始和对称的双倍大小的列表 从那以后我们首先只返回“长度”。

答案 1 :(得分:2)

List.last可能不会删除列表的最后一个元素。你可以做这样的事情,而不是去掉最后一个元素:

let rec reshuffle list: List<int> =
  match list with
  | [] -> []
  | head :: tail -> let reverseList = tail |> List.rev
                    match reverseList with
                    | [] -> [head]
                    | lastElement::rest -> [head; lastElement] @ reshuffle (List.rev rest)

请注意,这是具有多个List.rev的效率相当低的代码,但至少应该按照您的意愿执行。那些列表看起来似乎不是这个任务的一个非常好的数据结构,并且在内部使用数组似乎会好得多。

答案 2 :(得分:2)

以下是对代码进行少量修改的解决方案:

open System

let rec shuffle lst = 
  match lst |> List.length with
  | 0 | 1 -> lst
  | _ -> [lst |> List.head; lst |> List.last] @ shuffle lst.[1..lst.Length - 2]


[<EntryPoint>]
let main argv = 

  let data = [1..20]

  shuffle data |> List.iter (printf "%d, ")

  Console.ReadLine() |> ignore

  0 // return an integer exit code

答案 3 :(得分:2)

谢谢各位回复。我终于找到了这个解决方案:

let rec reshuffle list: List<int> =
  match list with
  | [] -> []
  | head :: tail -> head :: reshuffle (List.rev tail)

答案 4 :(得分:0)

不是最有效但恕我直言。

match [1;2;3;4;5;6;7;8;9;10] with
| [] -> []
| l -> l |> List.rev |> List.tail |> List.rev

答案 5 :(得分:0)

希望得到这个帮助。

let rec shuffle l =
    match l with
    | [] -> []
    | head::rest -> head::(shuffle (List.rev rest));;

shuffle [1.0..5.0];;

答案 6 :(得分:0)

第一行将最后一项作为一个项的列表返回 然后,您可以使用List.head选择最后一个项目,该项目将在第二行中返回一个项目列表的第一项。

let lastItemList = List.toSeq xs |> Seq.skip (xs.Length - 1) |> Seq.toList 
let lastItem = List.head lastItemList

答案 7 :(得分:0)

实际上可以只两次遍历列表:

  • 反转列表并一次确定其长度
  • 以连续通过方式构造结果,以避免在额外通过(可能被视为作弊)的过程中反转结果
let reshuffle xs =
    let rec revLen i acc = function
    | [] -> acc, i
    | x::xs -> revLen (i + 1) (x::acc) xs

    let rec shuffle k = function
    | 0, 0, _, _ -> k []
    | 0, 1, x::xs, _ -> k [x]
    | n, m, x::xs, y::ys -> shuffle (k << fun zs -> x::y::zs) (n - 1, m, xs, ys)
    | _ -> failwith "impossible" 

    let (rs, n) = revLen 0 [] xs
    shuffle id (n / 2, n % 2, xs, rs) 

示例:

reshuffle [1..5]
// val it : int list = [1; 5; 2; 4; 3]