我试图通过采用替代元素将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到'列表的函数。有人能告诉我哪里出错了吗?
答案 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)