【问题标题】:How can i convert between F# List and F# Tuple?如何在 F# List 和 F# Tuple 之间进行转换?
【发布时间】:2010-05-27 10:01:04
【问题描述】:

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

例如:

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

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

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

提前谢谢你。

【问题讨论】:

  • 这将是一个难题,因为元组是静态调整大小的,而列表是动态调整大小的。如果你真的需要这样的解决方案,我认为 sepp2k 的答案可能是最好的......为每个大小定义一个函数,直到你需要的最大列表大小,然后根据列表长度选择要调用的函数。跨度>

标签: f#


【解决方案1】:

正如已经指出的,这是一个棘手的问题,因为元组不是单一类型 - 它是一系列类型,例如 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 中的例子更清晰(它是动态的,因此不必处理静态类型安全)。

【讨论】:

  • 当然还有一点,元组不只是(int * int * int),还有(int * float * string),它不能很好地与列表结合。
  • 我正在努力寻找以下语法的任何文档。你能帮忙吗?让 le = 函数 | [x] -> x | _ -> 以“不兼容”失败
  • @TridentD'Gao 函数语法是匹配的语法糖。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

    嗯,它不是很漂亮,但是:

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

    我不知道你会怎么回去。我真的不确定这是否明智,但如果你真的必须这样做的话。

    【讨论】:

    • 嗯。这似乎只适用于字符串可序列化元组容器。不确定我喜欢这个解决方案。
    • 是的,它非常hacky,但我只有五分钟的时间来证明这个概念。 Huusom 的解决方案要好得多。
    【解决方案4】:

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

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

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        猜你喜欢
        • 2021-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多