如何在F#List和F#Tuple之间进行转换?

时间:2010-05-27 10:01:05

标签: f#

有没有办法在F#List和F#Tuple之间进行转换?

例如:

[1;2;3] -> (1,2,3)    
(1,2,3,4) -> [1;2;3;4]

我需要两个函数来做到这一点:

let listToTuple list = ...
let tupleToList tuple = ...

提前谢谢。

5 个答案:

答案 0 :(得分:18)

除了listToTuple,那么pblasucci有正确的答案。 但是你不会对结果感到满意,除非你知道所涉及的类型类型,或者如果你不想进行大量的装箱和拆箱。

let tupleToList t = 
    if Microsoft.FSharp.Reflection.FSharpType.IsTuple(t.GetType()) 
        then Some (Microsoft.FSharp.Reflection.FSharpValue.GetTupleFields t |> Array.toList)
        else None

let listToTuple l =
    let l' = List.toArray l
    let types = l' |> Array.map (fun o -> o.GetType())
    let tupleType = Microsoft.FSharp.Reflection.FSharpType.MakeTupleType types
    Microsoft.FSharp.Reflection.FSharpValue.MakeTuple (l' , tupleType)

答案 1 :(得分:13)

正如已经指出的,这是一个棘手的问题,因为元组不是单一类型 - 它是一类类型,如int * int * intint * int,而F#不提供任何方式把整个家庭作为一个论点。您可以编写许多类似的函数(非常不舒服)或使用反射(这有点慢并且不是类型安全的。)

或者,您可以将函数限制为具有某种结构的元组 - 例如,您可以使用(1, 2, 3, 4)之类的嵌套元组,而不是使用(1, (2, (3, 4)))。这有点不太舒服,但它保持了类型安全,并没有那么糟糕。

然后你可以轻松地编写组合器来动态构建转换函数:

// creates function for converting tuple (possibly with a nested 
// tuple in the second component to list
let tl f (a, b) = a::(f b)
// converts last element of the tuple to singleton list
let te a = [a]

然后你可以组合函数tlte来创建一个类型安全的函数,它将包含4个元素的嵌套元组转换为如下列表:

let l = (1, (2, (3, 4))) |> (tl (tl (tl te)))

类似地,您可以创建将列表转换为元组的函数 - 请注意,如果列表与预期格式不匹配,这可能会引发异常:

let le = function
  | [x] -> x
  | _ -> failwith "incompatible"
let lt f = function
  | [] -> failwith "incompatible"
  | x::xs -> (x, f xs) 

// convert list to a tuple of four elements
let t = [1; 2; 3; 4] |> lt (lt (lt le))

我想这可能与类型安全和可重用函数一样接近,可以在元组和列表之间进行转换。这根本不是完美的,但这是因为你试图实现一个很少使用的操作。在F#中,元组和列表之间的区别比Python中更明显(Python是动态的,因此它不必处理静态类型安全性)。

答案 2 :(得分:4)

实际上,您需要2*n个函数,其中n是您想要支持的最大元组大小。包含三个整数的元组与包含四个整数的元组完全不同,因此您需要分别为每个元组编写一个tupleToList和一个listToTuple函数。

另请注意,对于listToTuple,您需要知道要在编译时获取的元组的大小。即你不能创建一个函数来决定是否返回(int, int, int)(int, int),具体取决于输入列表的长度(因为正如我所说,它们是完全不同的类型)。你必须有一个函数listToNTuple,它取一个至少N个元素的列表并返回一个N元组。

有可能通过使用反射来为此编写与大小无关的函数,但由于您无法知道在编译时由此类函数返回的任何元组的类型,因此使用它会非常痛苦。

答案 3 :(得分:2)

利用PropertyInfo结构,可以递归地构建列表。这种方法的一个问题是类型信息丢失,结果作为obj列表产生。尽管如此,这确实解决了列出问题部分的元组。

let tupleToList tpl = 
    let rec loop tpl counter acc =
        let getItemPropertyInfo t n = t.GetType().GetProperty(sprintf "Item%d" n)
        let getItem t n = (getItemPropertyInfo t n).GetValue(t,null)
        match counter with
        | 8 -> 
            match tpl.GetType().GetProperty("Rest") with
            | null -> acc
            | _ as r ->
                let rest = r.GetValue(tpl,null)
                loop rest 2 ((getItem rest 1) :: acc)
        | _ as n -> 
            match getItemPropertyInfo tpl n with
            | null -> acc
            | _ as item -> loop tpl (counter+1) (item.GetValue(tpl,null) :: acc)
    loop tpl 1 [] |> List.rev

答案 4 :(得分:-1)

嗯,它不漂亮,但是:

let tuple_to_array input =
    let temp_str = input.ToString()
    temp_str.Substring(1, temp_str.Length - 2)
    |> Array.map (fun (x:string) -> x.TrimStart(' '))

我不知道你怎么回事。我真的不确定这是否合理,但如果你真的需要。