luk*_*san 7 f# base-class-library
在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))
这很奇怪,因为Tuple在任何一种情况下,编译的F#代码似乎都用来表示它的参数.那么为什么F#编译器告诉我类型不兼容?
为什么这有关系?好吧,基本上我想编写一个带有重载的方法来支持任意长度的元组.使用F#的语法元组是不可能的,因为必须提前知道参数的确切数量.但是,它似乎可以通过使用BCL Tuple类型,因为那些使用TRest技巧来允许任意长度的元组.不幸的是,如果我以这种方式编写我的重载,那么它们将无法使用F#语法元组,这是最终目标.
所以我的问题是:为什么语法元组和BCL元组不兼容?而且,是否有任何编写函数和/或方法的例子在F#中对任意长度的元组进行操作?
具体的应用程序处理我正在编写的基于类型推断的二进制解析库.您可以在此处查看代码.你可以看到我对元组的许多重载,但我不想将它们扩展到一些神奇的数字.
我认为你对非常长的元组的观察部分地回答了你的问题 - 在F#中,你被允许有任意长度的元组,所以创建一个包含9个元素的元组是完全没问题的:
let t = (1,1,1,1,1,1,1,1,1)
Run Code Online (Sandbox Code Playgroud)
如果你使用t.GetType()then 来查看运行时类型,那么这实际上是编译为嵌套的.NET元组Tuple<int, int, int, int, int, int, int, Tuple<int, int>>.
我不确定这是否是明确的答案,但我认为它显示了部分问题 - 如果F#元组匹配.NET元组,那么它们必须限制为8个元素(以匹配.NET元组类型)或者它们将是一个"漏洞"抽象,大型元组会(默默地)匹配一些嵌套的元组类型.
如果你需要一个适用于任意数量元素的函数,那么将参数作为列表而不是元组接受可能更有意义吗?或者你可以使用F#reflection(in Microsoft.FSharp.Reflection)编写一个适用于任意大小的元组的函数......但我发现这对解析器很有用,而其他方法可能不太好.
像往常一样救援的F#规范:
6.3.2元组表达式
形式为expr1,...,exprn的表达式是一个元组表达式。例如:
let three = (1,2,"3")
let blastoff = (10,9,8,7,6,5,4,3,2,1,0)
Run Code Online (Sandbox Code Playgroud)
对于新鲜类型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 F#中将元组添加到BCL之前,使用FSharp.Core dll中定义的System.Tuple类型
我猜您处理具有任意大小的元组的唯一方法是诉诸于构造和解构,然后使用来自 Microsoft.FSharp.Reflection.FSharpType\FSharpValue