【问题标题】:Splitting a list by alternating elements通过交替元素拆分列表
【发布时间】:2013-01-30 17:04:34
【问题描述】:

我正在尝试通过采用备用元素将 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 到 'a 列表的函数。谁能告诉我哪里出错了?

【问题讨论】:

    标签: f#


    【解决方案1】:

    杰克很好地解释了问题所在。这是一次匹配两个元素的替代解决方案。 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])
    

    【讨论】:

    • 谢谢。我给杰克打勾以获得正确的解决方案,因为我需要的主要问题是解释我出错的地方,但你的解决方案非常好。
    【解决方案2】:

    这是一个固定版本:

    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
    

    【讨论】:

    • List.length 是 O(N) - 不是 可以放入循环的东西。
    • 原来如此。出于某种原因,我认为 F# 实现将列表的长度存储在私有字段中以便快速访问。好吧,既然不是这样——@gradbot 的解决方案会更快。
    • 感谢您的解释。如您所知,我对 F# 还是很陌生!
    【解决方案3】:

    最简单的解决方案不是尾递归,但很好理解:

    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)
    

    【讨论】:

    • -1: 如果列表有奇数个元素,| [] | [_] -> [] 将丢失最后一个元素。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-14
    • 1970-01-01
    • 2019-04-08
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 1970-01-01
    相关资源
    最近更新 更多