将列表拆分为两个

时间:2012-02-01 22:24:15

标签: list f# f#-scripting

我想实现一个将大小为n和列表作为输入的函数。此函数会将列表切割为两个列表,一个是大小n,另一个是另一个列表。我是这门语言的新手,很难学习语法。

我遇到的主要问题是找到一种表达列表大小而不使用任何循环或可变变量的方法。

任何人都可以给我一些指示吗?

4 个答案:

答案 0 :(得分:10)

让我们从函数的类型签名开始。由于它获得n并且列表作为参数并返回一对列表,因此您有一个函数split

val split : int -> 'a list -> 'a list * 'a list

以下是实现此功能的一种方法:

let split n xs =
  let rec splitUtil n xs acc =
    match xs with
    | [] -> List.rev acc, []
    | _ when n = 0 -> List.rev acc, xs
    | x::xs' -> splitUtil (n-1) xs' (x::acc)
  splitUtil n xs []

我们的想法是使用累加器acc来保存已遍历的元素,并在此过程中逐渐减少n。因为元素前置于acc,所以最后你必须将其反转以获得正确的顺序。

该函数有两个基本案例要终止:

  • 此时没有任何元素可以遍历(xs = [])。
  • 您已浏览了列表中的第一个n元素(当时n减少到0)。

以下是split如何计算结果的简短说明:

   split 2 [1; 2; 3] // call the auxiliary function splitUtil
~> splitUtil 2 [1; 2; 3] [] // match the 3rd case of x::xs'
~> splitUtil 1 [2; 3] [1] // match the 3rd case of x::xs'
~> splitUtil 0 [3] [2; 1] // match the 2nd case of n = 0 (base case)
~> List.rev [2; 1], [3] // call List.rev on acc
~> [1; 2], [3]

答案 1 :(得分:8)

let split n list =
  let rec not_a_loop xs = function
    | (0, ys) | (_, ([] as ys)) -> (List.rev xs), ys
    | (n, x::ys) -> not_a_loop (x::xs) (n-1, ys)
  not_a_loop [] (n, list)

答案 2 :(得分:1)

新解决方案 - splitAt现已内置到List和Array中。请参阅github上的2014年左右提交。今天我在VS.2015中使用F#时注意到了这一点

现在你可以简单地做到这一点......

let splitList n list = 
    List.splitAt n list

正如您所料,签名是......

n: int -> list: 'a list -> 'a list * 'a list

使用示例:

let (firstThree, remainder)  = [1;2;3;4;5] |> (splitList 3)
printfn "firstThree %A" firstThree
printfn "remainder %A" remainder

输出:

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

Github感兴趣的人:https://github.com/dsyme/visualfsharp/commit/1fc647986f79d20f58978b3980e2da5a1e9b8a7d

答案 3 :(得分:0)

另一种方法,使用fold

let biApply f (a, b) = (f a, f b)

let splitAt n list = 
  let splitter ((xs, ys), n') c =
    if n' < n then
      ((c :: xs, ys), n' + 1)
    else
      ((xs, c :: ys), n' + 1)
  List.fold splitter (([], []), 0) list 
  |> fst 
  |> biApply List.rev

Here是关于折叠的精彩系列,而不是您可以了解有关该主题的更多内容。