我正在编写一个将采用整数列表的函数,例如:
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]
似乎这是因为每次递归完成函数时,都不会删除最后一个元素。如何删除函数中的头部和尾部?
答案 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]