List 将列表一分为二

List 将列表一分为二,list,f#,f#-scripting,List,F#,F# Scripting,我想实现一个函数,将大小n和列表作为输入。此函数将列表剪切为两个列表,一个大小为n,其余的在另一个列表中。我对这种语言不熟悉,学习语法很困难 我遇到的主要问题是找到一种方法来表示列表的大小,而不使用任何循环或可变变量 谁能给我一些建议吗?让我们从函数的类型签名开始。由于它获取n和一个列表作为参数,并返回一对列表,因此有一个函数split: let split n list = let rec not_a_loop xs = function | (0, ys) | (_, ([] a

我想实现一个函数,将大小n和列表作为输入。此函数将列表剪切为两个列表,一个大小为n,其余的在另一个列表中。我对这种语言不熟悉,学习语法很困难

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


谁能给我一些建议吗?

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

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)
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]

还有一种方法,使用
折叠

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
设双应用f(a,b)=(fa,fb)
让splitAt n list=
let splitter((xs,ys),n')c=
如果n'fst
|>biApply List.rev

这是一个关于折叠的伟大系列,您可以通过它了解更多有关该主题的信息。

新的解决方案-splitAt现在内置于列表和数组中。参见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面向感兴趣的用户:

您尝试了什么?至少你应该给我们一个非工作版本来展示你的努力?提示:你不需要表达列表的长度-你所需要的只是一种减少
n
,并检查它是否已达到零的方法。这可能有点离题,但有一个相当巧妙的解决方案(由Juliet提出)将列表一分为二,在不预先知道/指定长度的情况下:我不太理解您的代码,但它似乎使用了循环。我正在考虑做一些递归调用,其中我将指定n作为索引,并在找到位置n中的项时将其作为基本大小写返回。。你认为呢?这或多或少就是它的作用,它在
n
之前累积元素。@user1072706:不,它使用一个名为loop的递归函数。名称是任意的,不要让它迷惑你。谢谢,语句|(|,([]as ys))翻译成什么?这里有一定的讽刺意味,考虑到内部函数是尾部递归的,因此最有可能编译成循环。感谢您的详细解释。最后一个“splitUtil n xs[]扮演什么角色?”在函数中播放?@user1072706:这是一个辅助函数,用于避免使用一个始终为空列表的额外参数声明
split
。您可以直接使用
splitUtil
,然后一切都从第二行开始。@user1072706,它调用内部函数(
splitUtil
),外部函数(
split
)的初始参数加上一个空的
acc
)来填充。这个函数不是遍历整个列表,而不是最多n个吗?