如何在f#列表和f# Tuple之间进行转换?

时间:2022-02-08 12:56:53

Is there some way to convert between F# List and F# Tuple?

在f#列表和f# Tuple之间有什么转换方法吗?

For example:

例如:

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

I need two Functions to do that:

我需要两个函数:

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

Thank you in advance.

提前谢谢你。

5 个解决方案

#1


16  

Besides listToTuple then pblasucci has the right answer. But you wont be happy with the result unless you know something about type types involved, or if you wan't to do a lot of boxing and unboxing.

除了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)

#2


11  

As already pointed out, this is a tricky problem, because tuple isn't a single type - it is a family of types such as int * int * int or int * int and F# doesn't provide any way for taking the whole family of types as an argument. You can either write many similar functions (which is very uncomfortable) or use reflection (which is a bit slow and isn't type-safe).

如前所述,这是一个棘手的问题,因为tuple不是单个类型——它是一系列类型,比如int * int * int * int或int * int,而f#没有提供任何方法将整个类型系列作为参数。您可以编写许多类似的函数(这很不舒服),也可以使用反射(这有点慢,而且不是类型安全的)。

Alternatively, you could limit the function to tuples with some structure - for example, instead of working with (1, 2, 3, 4), you could use nested tuples like (1, (2, (3, 4))). This is a bit less comfortable, but it keeps the type safety and it isn't as bad.

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

Then you can easily write combinators for constructing conversion functions on the fly:

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

// 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]

Then you can combine functions tl and te to create a type-safe function that converts nested tuple containing 4 elements to a list like this:

然后,您可以将函数tl和te组合起来,创建一个类型安全的函数,该函数将包含4个元素的嵌套元组转换为如下列表:

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

Similarly, you can create functions for converting list to tuples - note that this may throw an exception if the list doesn't match the expected format:

同样,您可以创建将列表转换为元组的函数——注意,如果列表与预期的格式不匹配,这可能会抛出异常:

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))

I guess this is probably as close to typesafe and reusable function for converting between tuples and lists as it can get. It isn't perfect (at all), but that's caused by the fact that you're trying to implement a very rarely used operation. In F#, the distinction between tuples and lists is clearer than for example in Python (which is dynamic, so it doesn't have to deal with static type safety).

我想这可能与类型安全和可重用函数接近,可以在元组和列表之间进行转换。它并不完美(一点也不完美),但这是因为您正在尝试实现一个很少使用的操作。在f#中,元组和列表之间的区别比在Python中(它是动态的,所以它不需要处理静态类型安全)更加明显。

#3


4  

Actually you need 2*n functions where n is the greatest tuple size you want to support. A tuple containing three ints has a completely different type than a tuple containing four ints, so you need to write a tupleToList and a listToTuple function for each one separately.

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

Also note that you for listToTuple you need to know the size of the tuple you want to get at compile-time. I.e. you can't create a function that decides whether to return (int, int, int) or (int, int) depending on the length of the input list (since as I said, they're totally different types). You'd have to have a function listToNTuple that takes a list of at least N elements and returns a N-tuple.

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

It might be possible to write size-independent functions for this by using reflection, but since you couldn't know the type of any tuple returned by such a function at compile-time, it'd be quite painful to use.

也许可以通过使用反射来为它编写大小无关的函数,但是由于您无法知道这样一个函数在编译时返回的任何tuple的类型,因此使用它是非常痛苦的。

#4


2  

Making use of the PropertyInfo structure one can build a list recursively. A problem with this approach is that the type information is lost and the result is produced as a list of obj. Nevertheless, this does solve the tuple to list portion of the question.

利用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

#5


0  

Well, it ain't pretty but:

不是很漂亮,但是

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(' '))

I'm not sure how you'd go back the other way though. And I'm really not sure this would be sensible, but if you really have to.

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

#1


16  

Besides listToTuple then pblasucci has the right answer. But you wont be happy with the result unless you know something about type types involved, or if you wan't to do a lot of boxing and unboxing.

除了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)

#2


11  

As already pointed out, this is a tricky problem, because tuple isn't a single type - it is a family of types such as int * int * int or int * int and F# doesn't provide any way for taking the whole family of types as an argument. You can either write many similar functions (which is very uncomfortable) or use reflection (which is a bit slow and isn't type-safe).

如前所述,这是一个棘手的问题,因为tuple不是单个类型——它是一系列类型,比如int * int * int * int或int * int,而f#没有提供任何方法将整个类型系列作为参数。您可以编写许多类似的函数(这很不舒服),也可以使用反射(这有点慢,而且不是类型安全的)。

Alternatively, you could limit the function to tuples with some structure - for example, instead of working with (1, 2, 3, 4), you could use nested tuples like (1, (2, (3, 4))). This is a bit less comfortable, but it keeps the type safety and it isn't as bad.

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

Then you can easily write combinators for constructing conversion functions on the fly:

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

// 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]

Then you can combine functions tl and te to create a type-safe function that converts nested tuple containing 4 elements to a list like this:

然后,您可以将函数tl和te组合起来,创建一个类型安全的函数,该函数将包含4个元素的嵌套元组转换为如下列表:

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

Similarly, you can create functions for converting list to tuples - note that this may throw an exception if the list doesn't match the expected format:

同样,您可以创建将列表转换为元组的函数——注意,如果列表与预期的格式不匹配,这可能会抛出异常:

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))

I guess this is probably as close to typesafe and reusable function for converting between tuples and lists as it can get. It isn't perfect (at all), but that's caused by the fact that you're trying to implement a very rarely used operation. In F#, the distinction between tuples and lists is clearer than for example in Python (which is dynamic, so it doesn't have to deal with static type safety).

我想这可能与类型安全和可重用函数接近,可以在元组和列表之间进行转换。它并不完美(一点也不完美),但这是因为您正在尝试实现一个很少使用的操作。在f#中,元组和列表之间的区别比在Python中(它是动态的,所以它不需要处理静态类型安全)更加明显。

#3


4  

Actually you need 2*n functions where n is the greatest tuple size you want to support. A tuple containing three ints has a completely different type than a tuple containing four ints, so you need to write a tupleToList and a listToTuple function for each one separately.

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

Also note that you for listToTuple you need to know the size of the tuple you want to get at compile-time. I.e. you can't create a function that decides whether to return (int, int, int) or (int, int) depending on the length of the input list (since as I said, they're totally different types). You'd have to have a function listToNTuple that takes a list of at least N elements and returns a N-tuple.

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

It might be possible to write size-independent functions for this by using reflection, but since you couldn't know the type of any tuple returned by such a function at compile-time, it'd be quite painful to use.

也许可以通过使用反射来为它编写大小无关的函数,但是由于您无法知道这样一个函数在编译时返回的任何tuple的类型,因此使用它是非常痛苦的。

#4


2  

Making use of the PropertyInfo structure one can build a list recursively. A problem with this approach is that the type information is lost and the result is produced as a list of obj. Nevertheless, this does solve the tuple to list portion of the question.

利用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

#5


0  

Well, it ain't pretty but:

不是很漂亮,但是

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(' '))

I'm not sure how you'd go back the other way though. And I'm really not sure this would be sensible, but if you really have to.

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