通过交替元素拆分列表

时间:2013-01-30 17:04:34

标签: f#

我试图通过采用替代元素将F#列表拆分为两个。这是我的尝试:

let split l =  
    let rec loop l isEven result1 result2 =  
        match l with  
        | [] -> result1 result2  
        | [head::tail] when isEven -> loop tail (not isEven) head::result1 result2  
        | [head::tail] -> loop tail (not isEven) result1 head::result2  
    loop l false [] []

这给了我一个错误:

Program.fs(5,39): error FS0001: Type mismatch. Expecting a  
    'a    
but given a  
    'b -> 'a list      
The resulting type would be infinite when unifying ''a' and ''b -> 'a list'

我不知道它是如何无限的,我不明白为什么它认为我给它一个从'b到'列表的函数。有人能告诉我哪里出错了吗?

3 个答案:

答案 0 :(得分:6)

杰克很好地解释了什么是错的。这是一个可以一次匹配两个元素的替代解决方案。 F#的pattern matching documentation有很多很好的例子。

let split list = 
    let rec split odd even list =
        match list with
        | a::b::tail -> split (a::odd) (b::even) tail
        | a::tail -> split (a::odd) even tail
        | [] -> List.rev odd, List.rev even

    split [] [] list

示例输出。

printfn "%A" (split [1 .. 10])
System.Console.ReadLine() |> ignore

([1; 3; 5; 7; 9], [2; 4; 6; 8; 10])

答案 1 :(得分:5)

这是一个固定版本:

let rec loop l isEven result1 result2 =
    match l with
    | [] ->
        result1, result2
    | head :: tail when isEven ->
        loop tail (not isEven) (head :: result1) result2
    | head :: tail ->
        loop tail (not isEven) result1 (head :: result2)
  • 在第一种情况([])中,我添加了一个逗号,因为loop函数需要将值作为元组返回。如果没有逗号,您基本上将result1视为一个函数并将result2应用于它。
  • 空列表模式是正确的([])但在其他情况下,您不使用括号 - 只是缺点(::)模式。
  • 您需要将head :: result括在括号中,否则F#会像您编写的那样读取代码:(loop tail (not isEven) head) :: (result1 result2)

哦,如果你想要返回的列表与原始列表的顺序相同,则在返回列表时需要使用List.rev,如下所示:

match l with
| [] ->
    List.rev result1, List.rev result2

最后,这是一个稍微简化的函数版本 - 你真的不需要isEven参数来使函数工作。相反,您只是尝试使列表保持相同的长度:

let rec loop (result1, result2) l =
    match l with
    | [] ->
        List.rev result1, List.rev result2
    | hd :: tl ->
        if List.length result1 = List.length result2 then
            loop (hd :: result1, result2) tl
        else
            loop (result1, hd :: result2) tl

答案 2 :(得分:1)

最简单的解决方案不是尾递归,而是非常易于理解:

let prepend2 (x, y) (xs, ys) = x::xs, y::ys

let rec split = function
  | [] | [_] as xs -> xs, []
  | x0::x1::xs -> prepend2 (x0, x1) (split xs)