有没有办法在F#List和F#Tuple之间进行转换?
例如:
[1;2;3] -> (1,2,3)
(1,2,3,4) -> [1;2;3;4]
我需要两个函数来做到这一点:
let listToTuple list = ...
let tupleToList tuple = ...
提前谢谢。
答案 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 * int
或int * 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]
然后你可以组合函数tl
和te
来创建一个类型安全的函数,它将包含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(' '))
我不知道你怎么回事。我真的不确定这是否合理,但如果你真的需要。