如何在F#中创建一个值类型的联合类型?

时间:2015-07-14 11:22:13

标签: c# .net pointers f# discriminated-union

正常F#区分联合是引用类型。如何在F#中创建一个简单(非递归且仅具有值类型字段)联合类型作为值类型?

基于某些互联网搜索,我当前(非工作)的尝试如下:

[<StructLayout(LayoutKind.Explicit)>]
type Float =
    [<DefaultValue>] [<FieldOffset 0>] val mutable Val1 : float
    [<DefaultValue>] [<FieldOffset 0>] val mutable Int1 : int
    new (a:float) = {Val1 = a}    

The following blog post appears to show what is possible via C#

我知道上面的内容不是惯用的F#但是我试图优化我的部分应用程序的性能,并且分析清楚地表明堆分配的成本(JIT_new)是导致我的性能的原因瓶颈...简单的联合类型是满足我需求的完美数据结构,而不是堆分配的。

2 个答案:

答案 0 :(得分:11)

首先,我可能不会这样做,除非我有非常的理由。在大多数情况下,结构和引用类型之间的区别并不是那么大 - 根据我的经验,它只对你有一个非常大的数组(然后结构允许你分配一个大的内存块)很重要。

那就是说,看起来F#不喜欢你的例子中的构造函数代码。我真的不确定为什么(它似乎正在做一些对重叠结构不起作用的检查),但以下是诀窍:

[<Struct; StructLayout(LayoutKind.Explicit)>]
type MyStruct =
    [<DefaultValue; FieldOffset 0>] 
    val mutable Val1 : float
    [<DefaultValue; FieldOffset 0>] 
    val mutable Int1 : int
    static member Int(a:int) = MyStruct(Int1=a)
    static member Float(f:float) = MyStruct(Val1=f)

如果我真的想要使用它,我会添加另一个包含Tag1的字段0,具体取决于您的结构所代表的情况。然后,您可以使用活动模式对其进行模式匹配,并获得一些有区别的工会的安全性:

let (|Float|Int|) (s:MyStruct) = 
  if s.Tag = 0 then Float(s.Val1) else Int(s.Int1)

答案 1 :(得分:0)

F#现在支持结构联合,有关详细信息,请参见F# RFC FS-1014。简而言之:

// Single case:

[<Struct>]
type UnionExample = U of int * int * bool

// Multi-case:

[<Struct>]
type Shape =
   | Circle of radius: double
   | Square of side: int
     

结构记录中的主要区别:

     
      
  • 您不能具有对相同类型的循环引用。例如:类型T = T的U
  •   
  • 您也不能像使用常规F#结构那样调用默认的ctor。
  •   
  • 对于多案例结构联合,每个案例必须具有唯一的名称。
  •