多类型列表

Wor*_*ice 3 f# types list

我构建了一个函数,给定一个组合列表,返回两个列表:

let rec split2 l =
    match l with 
    [] -> ([], [])
    | (x, y)::ls -> let (xs, ys) =
                                split ls in (x::xs, y::ys);;  

val split2 : l:('a * 'b) list -> 'a list * 'b list

lsts = [('a', 1); ('b', 2); ('c', 3); ('d', 4)]

split2 lsts;;
val it : int list * char list = ([1; 2; 3; 4], ['a'; 'b'; 'c'; 'd'])
Run Code Online (Sandbox Code Playgroud)

现在,我将该概念应用于更复杂的列表:

let l1 = [('a', 1, 'a'); ('b', 2, 'b'); ('c', 3, 'c'); ('d', 4, 'd')]
Run Code Online (Sandbox Code Playgroud)

我使用的函数给出了类型的问题,所以我构建了第二个.在这种情况下,我已经仔细定义了类型,但是在应用时仍会返回错误l1,即使它已编译.

let rec split3 (l:(char * int * char) list) =                  
    match l with 
    [] -> ([], [], [])
    | (x, y, z)::ls -> 
                    let (xs, ys, zs) = 
                                    split3 ls in (xs, ys, zs);; 

val split3 : l:(char * int * char) list -> 'a list * 'b list * 'c list

split3 l1;;

    error FS0030: Value restriction. The value 'it' has been inferred to 
    have generic type val it : '_a list * '_b list * '_c list    
    Either define 'it' as a simple data term, make it a function with explicit 
arguments or, if you do not intend for it to be generic, add a type annotation.
Run Code Online (Sandbox Code Playgroud)

为什么,即使声明了类型,它还需要进一步的类型注释?

The*_*ght 6

简答

你已经在寻找的函数存在FSharp.CoreList.unzip3.

List.unzip3 : ('T1 * 'T2 * 'T3) list -> 'T1 list * 'T2 list * 'T3 list
Run Code Online (Sandbox Code Playgroud)

答案很长

您描述的两个功能是不同的.请注意,在split3函数的类型签名中是:

val split3 : l:(char * int * char) list -> 'a list * 'b list * 'c list
Run Code Online (Sandbox Code Playgroud)

这没有意义.类型签名应该是:

val split3 : l:(char * int * char) list -> char list * int list * char list
Run Code Online (Sandbox Code Playgroud)

那么,为什么不呢?

请注意,在您的split2函数中,您已将结果定义为,(x::xs, y::ys)并且split3已将结果定义为(xs, ys, zs).这意味着您的split3函数的结果始终是,([], [], [])但未定义空列表的类型 - 因此值限制错误.

这很容易解决:

let rec split3 (l:(char * int * char) list) =                  
    match l with 
    | [] -> ([], [], [])
    | (x, y, z)::ls -> 
        let (xs, ys, zs) = split3 ls
        (x::xs, y::ys, z::zs)
Run Code Online (Sandbox Code Playgroud)

一旦你纠正了这个,就可以删除类型注释,因为现在可以正确推断出函数的类型:

let rec split3 l =                  
    match l with 
    | [] -> ([], [], [])
    | (x, y, z)::ls -> 
        let (xs, ys, zs) = split3 ls
        (x::xs, y::ys, z::zs)
Run Code Online (Sandbox Code Playgroud)

此外,这种类型的函数只是fold如此,如果你手动编写它,最好用高阶函数而不是通过显式递归来编写它.

let split3 l =
    let folder (x, y, z) (xs, ys, zs) =
        (x::xs, y::ys, z::zs)
    List.foldBack folder l ([], [], [])
Run Code Online (Sandbox Code Playgroud)

请注意,我正在使用foldBack而不是fold保留原始列表顺序.