在F#中,您可以按如下方式定义first
函数:
let first (x, y) = x
您可以这样称呼它:
first (1, 2)
您还可以根据BCL Tuple
类型定义相同的功能:
let first (t:Tuple<_, _ >) = t.Item1
但是,您无法使用先前的语法调用它,否则您将收到以下错误:
error FS0001: The type ''c * 'd' is not compatible with the type 'Tuple<'a,'b>'
相反,您必须执行以下操作:
first (Tuple<_,_>(1, 2))
这很奇怪,因为在任何一种情况下,编译的F#代码似乎都使用Tuple
来表示其参数。那么为什么F#编译器告诉我类型不兼容?
为什么这有关系?好吧,基本上我想编写一个带有重载的方法来支持任意长度的元组。使用F#的语法元组是不可能的,因为必须提前知道参数的确切数量。但是,它似乎可以通过使用BCL Tuple
类型,因为那些使用TRest
技巧来允许任意长度的元组。不幸的是,如果我以这种方式编写我的重载,那么它们将无法使用F#语法元组,这是最终目标。
所以我的问题是:为什么语法元组和BCL元组不兼容?而且,是否有任何编写函数和/或方法的例子在F#中对任意长度的元组进行操作?
特定的应用程序处理我正在编写的基于类型推断的二进制解析库。您可以查看代码here。你可以看到我对元组的许多重载,但我不想将它们扩展到一些神奇的数字。
答案 0 :(得分:5)
我认为你对非常长的元组的观察部分回答了你的问题 - 在F#中,你可以拥有任意长度的元组,所以创建一个包含9个元素的元组是完全没问题的:
let t = (1,1,1,1,1,1,1,1,1)
如果使用t.GetType()
查看运行时类型,则实际上将其编译为嵌套的.NET元组Tuple<int, int, int, int, int, int, int, Tuple<int, int>>
。
我不确定这是否是明确的答案,但我认为它显示了问题的一部分 - 如果F#元组匹配.NET元组,那么它们必须限制为8个元素(以匹配.NET)元组类型)或者它们将是一个“漏洞”抽象,大元组会(静默地)匹配一些嵌套元组类型。
如果你需要一个适用于任意数量元素的函数,那么接受参数作为列表而不是元组可能更有意义吗?或者你可以使用F#reflection(在Microsoft.FSharp.Reflection
中)编写一个适用于任意大小元组的函数......但我发现这对解析器很有用,其他方法可能不太好。
答案 1 :(得分:4)
6.3.2元组表达式
表达式expr1,...,exprn是一个元组表达式。例如:
let three = (1,2,"3")
let blastoff = (10,9,8,7,6,5,4,3,2,1,0)
对于新类型ty1 ... tyn,表达式具有类型(ty1 * ... * tyn),并且使用初始类型tyi检查每个单独的表达式ei。
元组类型和表达式被转换为名为System.Tuple的一系列F#库类型的应用程序。元组类型ty1 * ... * tyn翻译如下:
Tuple<ty1,...,tyn>
。System.Tuple<_>
的应用的简写,如下所示:Tuple<ty1,...,ty7,Tuple<ty8>>
。Tuple<ty1,...,ty7,tyB>
,其中tyB是该类型的转换形式(ty8 ... tyn)。元组表达式(expr1,...,exprn)翻译如下:
new Tuple<ty1,…,tyn>(expr1,...,exprn)
。new Tuple<ty1,…,ty7,Tuple<ty8>>(expr1,...,expr7, new Tuple<ty8>(expr8)
。new Tuple<ty1,...ty7,ty8n>(expr1,..., expr7, new ty8n(e8n)
,其中ty8n是类型(ty8 * ... * tyn),expr8n是表达式的详细形式
expr8,...,exprn。当被视为静态类型时,元组类型与其编码形式不同。但是,元组值和类型的编码形式在F#类型系统中通过运行时类型可见。例如,typeof等效于typeof<System.Tuple<int,int>>
,而(1,2)具有运行时类型System.Tuple<int,int>
。同样,(1,2,3,4,5,6,7,8,9)具有运行时类型Tuple<int,int,int,int,int,int,int,Tuple<int,int>>
。
注意:在.NET 4.0中将元组添加到BCL之前F#使用FSharp.Core dll中定义的System.Tuple类型
我想你处理任意大小的元组的唯一方法是使用Microsoft.FSharp.Reflection.FSharpType\FSharpValue