在F#中建模复杂的层次结构

时间:2016-06-07 07:57:29

标签: f#

我对F#相当新,我想在现实世界中对相当复杂的东西进行建模" has-a"关系。在层次结构的顶部有四种类型,A - D,具有以下关系:

A
|
+--A
|
+--B
|  |
|  +--B
|  |
|  +--D
|     |
|     +--D
|
+--C
|  |
:  +--D
|     |
|     +--D
:

所以B型可以有一个"父母" A或B的类型,D类型可以具有B,C或D的父级。

我想使用受歧视的联盟来约束每种类型的父级,因此不能将它们分配给无效的父级,例如:

type B_Parent = A | B
type D_Parent = B | C | D

然后我想使用记录来为每种类型建模,其中一个字段是父类,例如:

type A = { parent:A_Parent; ... }
type B = { parent:B_Parent; ... }
type C = { parent:C_Parent; ... }
type D = { parent:D_Parent; ... }

C_Parent不是问题,因为它的父类型A是事先声明的。我已经使用过' A选项'对于A_Parent。但我还没有能够弄清楚如何定义B_Parent和D_Parent,因为它们对自己和其他类型的嵌套依赖?

2 个答案:

答案 0 :(得分:5)

首先,一个非常重要的事情:当你写作

type B_Parent = A | B

声明B_Parent是加入两个先前定义的类型AB的DU。没有语法。

上面的行实际上是定义B_Parent的两个空子类型,它们与原始AB无关。它相当于:

type B_Parent = B_Parent.A | B_Parent.B

为了在DU中重用现有类型,你需要给案例命名 - 有效地将它们包装在另一种类型中。所以适当的声明可以是:

type B_Parent = P_a of A | P_b of B

除此之外 - 正如Anton所说,用于定义相互引用类型的关键字是and。也就是说,最好保持这种相互参照尽可能小而紧。所以我们可以这样做:

type A = { parent:A_Parent; ... }
    and A_Parent = P_a of A
                 | None

type B = { parent:B_Parent; ... }
    and B_Parent = P_a of A 
                 | P_b of B

type C = { parent:C_Parent; ... }
    and C_Parent = P_a of A

type D = { parent:D_Parent; ... }
    and D_Parent = P_b of B 
                 | P_c of C 
                 | P_d of D

我们可能只使用A option用于A_Parent,而A仅用于C_Parent,但我认为保持案例名称一致可能会使事情更具可读性。

答案 1 :(得分:2)

您可以使用and关键字来定义相互关联的类型,例如:

type B_Parent = A | B
and D_Parent = B | C | D
and A_Parent = A
and C_Parent = B
and A = { parent:A_Parent }
and B = { parent:B_Parent }
and C = { parent:C_Parent }
and D = { parent:D_Parent }

另见related SO question。定义mutually recursive functions时,and关键字也很有用。