F#

时间:2018-02-09 21:56:47

标签: recursion f# functional-programming

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

Split list into two equal lists in F#

我正在尝试实施用户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)

如果模式匹配确实像if语句一样工作,那么它应该是,因为在if语句中它看起来像

 if (x::xs && y1::y2::ys)
       {
         x::zs
         cut (xs, ys)
       }

无论如何,它似乎不允许声明x :: zs,因为Visual Studio给了我一个警告:

  

隐式忽略此表达式的结果。考虑使用“忽略”来明确地丢弃此值,例如'expr |>忽略',或'让'将结果绑定到名称,例如让result = expr'。

我不确定最后一部分意味着什么。我以为我已经将列表声明为行

中函数的局部变量
let zs = []

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

2 个答案:

答案 0 :(得分:2)

  

无论如何,它似乎不允许声明x :: zs,因为Visual Studio会给我一个警告。

允许表达式 x::zs,但Visual Studio试图告诉您它没有效果。 x::zs会创建一个新列表,但会立即丢弃它(会改变现有值!)。

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

把这一切放在一起,我想你想要的是这样的:

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

注意:没有必要使用,来分隔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 []

答案 1 :(得分:1)

这是通过添加另一个累加器值来实现它的方法,该值将项构建到列表中:

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