Recursion F中递归列表拆分函数的困难#

Recursion F中递归列表拆分函数的困难#,recursion,f#,functional-programming,Recursion,F#,Functional Programming,我正在尝试实现一个函数,该函数用于在F#中将一个列表拆分为两个等长的一半(问题假设列表的长度为偶数)。使用搜索函数生成了一个线程,该线程处理与我现在尝试解决的问题完全相同的问题: 我正在尝试实现用户Juliet给出的解决方案,Juliet提供了部分答案: let cut l = let rec cut = function | xs, ([] | [_]) -> xs | [], _ -> [] | x::xs, y::y'::ys -> cut (x

我正在尝试实现一个函数,该函数用于在F#中将一个列表拆分为两个等长的一半(问题假设列表的长度为偶数)。使用搜索函数生成了一个线程,该线程处理与我现在尝试解决的问题完全相同的问题:

我正在尝试实现用户Juliet给出的解决方案,Juliet提供了部分答案:

let cut l =
let rec cut = function
    | xs, ([] | [_]) -> xs
    | [], _ -> []
    | x::xs, y::y'::ys -> cut (xs, ys)
cut (l, l)
返回列表的后半部分。我正试图找到一种方法返回上半场,所以我做了一些修改:

let rec cut(xs, ys) =
let zs = []
match xs, ys with
    | xs, ([] | [_]) -> (xs, zs)
    | [], _ -> ([], [])
    | x::xs, y1::y2::ys -> 
        x::zs
        cut (xs, ys)
你可能知道F#是我的第一种函数式编程语言,我对它的工作原理有点困惑。模式匹配对我来说是一件新鲜事,我从一开始就不太擅长递归

据我所知,模式匹配的工作方式是它基本上是if-then-else语句的一个更通用的版本,其中->左边的代码是有条件的(“if”位),右边的所有内容都是在条件检查通过时要执行的代码块(“then”位)

我不完全确定的是你是否被允许这样做:

| x::xs, y1::y2::ys -> 
        x::zs
        cut (xs, ys)
let rec cut(xs, ys, zs) =
    match xs, ys with
    | xs, ([] | [_]) -> (xs, zs)
    | [], _ -> ([], [])
    | x::xs, y1::y2::ys ->
        cut (xs, ys, x::zs)
如果模式匹配真的像If语句一样工作,那么它应该是这样的,因为在If语句中它看起来像

 if (x::xs && y1::y2::ys)
       {
         x::zs
         cut (xs, ys)
       }
无论如何,似乎不允许使用语句x::zs,因为Visual Studio给了我一个警告:

此表达式的结果被隐式忽略。考虑使用“忽略”来显式地丢弃这个值,例如“ExpR>>忽略”,或者“让”将结果绑定到一个名称,例如让结果= ExpR.< /P> 我不知道最后那部分是什么意思。我想我已经在行中将列表声明为函数的局部变量

let zs = []

我所要做的就是在cut函数的每次递归迭代中取列表xs的头部,并将其添加到另一个列表zs中,当达到基本情况时,该列表zs将包含传递的每个元素x(换句话说,列表的前半部分),然后返回两个xs(包含列表的后半部分)和包含前半部分的列表,但似乎不允许这样做?

这里有一种方法,通过添加另一个累加器值,将慢项累积到列表中:

let cut l =
    let rec loop acc rem =
        match rem with
        | xs, ([] | [_]) -> List.rev acc, xs
        | [], _ -> [], []
        | x::xs, y::y'::ys -> loop (x::acc) (xs, ys)
    loop [] (l, l)

> cut [1;2;3;4;5;6]
([1; 2; 3], [4; 5; 6])
无论如何,似乎不允许使用语句x::zs,因为VisualStudio给了我一个警告

表达式
x::zs
是允许的,但是VisualStudio试图告诉您它没有效果
x::zs
创建一个新列表,但随后立即丢弃它(它不改变现有值!)

根据提供的其余代码,
zs
应包含列表的前半部分,但
zs
将只包含空列表,因为它是唯一分配给它的值。在像C#这样的语言中,您可以简单地通过附加到列表来改变列表,但在F#中,您不应该这样做。由于您已经在使用递归,这表明
zs
应该是递归函数的参数,以便以后可以使用新值。(我发现有些人如果把递归看作回调,就会更好地理解它。当你递归时,就像给回调函数提供参数一样。你想在递归过程中积累的任何值都需要作为参数提供给你的函数。)

综上所述,我认为你想要的是这样的:

| x::xs, y1::y2::ys -> 
        x::zs
        cut (xs, ys)
let rec cut(xs, ys, zs) =
    match xs, ys with
    | xs, ([] | [_]) -> (xs, zs)
    | [], _ -> ([], [])
    | x::xs, y1::y2::ys ->
        cut (xs, ys, x::zs)
通常,您会将此函数隐藏在正确设置参数的非递归函数中:

let cut(xs, ys) =
    let rec impl(xs, ys, zs) =
        match xs, ys with
        | xs, ([] | [_]) -> (xs, zs)
        | [], _ -> ([], [])
        | x::xs, y1::y2::ys ->
            impl (xs, ys, x::zs)
    impl(xs, ys, [])

注意:不需要使用
s来分隔F#中的参数。当您这样做时,实际上是在声明一个函数有一个由元组组成的参数。这通常不是您想要的,它会阻止函数的重复。分隔参数只需使用空格:

let cut xs ys =
    let rec impl xs ys zs =
        match xs, ys with
        | xs, ([] | [_]) -> (xs, zs)
        | [], _ -> ([], [])
        | x::xs, y1::y2::ys ->
            impl xs ys (x::zs)
    impl xs ys []