联盟成员的子集为"参数"在模式匹配中

时间:2016-11-28 15:23:53

标签: f# discriminated-union active-pattern

让我们对具有多种类型的二进制节点的树进行类型定义,其中包括其他类型的节点,即

type Tree =
| BinaryNodeA of Tree * Tree
| BinaryNodeB of Tree * Tree
| [Other stuff...]

我想使用递归函数来操纵这个树,该递归函数可以例如交换任何类型的二进制节点的子节点(通过构造新节点)。让我疯狂的问题是:如何匹配所有BinaryNodes,以便Node风格成为"参数"有没有可以应用于任何BinaryNode风格的通用交换,以返回该风味的交换节点?

我知道如何使用活动模式匹配所有BinaryNodes树:

let (|BinaryNode|_|) (tree : Tree) =
    match tree with
    | BinaryNodeA _ | BinaryNodeB _ -> Some(tree)
    | _ -> None

但这并不够好,因为以下似乎无法实现:

match tree with
| [cases related to unary nodes..]
| BinaryNode a b -> BinaryNode b a

换句话说,我还没有找到使用BinaryNode风格的方法,就好像它是像a和b这样的参数。相反,似乎我必须分别匹配每个BinaryNode风格。如果存在大量二进制节点风格,这可能具有实际意义。对于Fsyacc / Fslex生成的解析器/词法分析器,类型树是AST,它限制了重构它的选项。有什么想法吗?

1 个答案:

答案 0 :(得分:5)

您只需要更改活动模式的定义:

type Flavor = A | B

let (|BinaryNode|_|) (tree : Tree) =
    match tree with
    | BinaryNodeA(x,y) -> Some(A,x,y) 
    | BinaryNodeB(x,y) -> Some(B,x,y)
    | _ -> None

let mkBinaryNode f t1 t2 =
    match f with
    | A -> BinaryNodeA(t1,t2)
    | B -> BinaryNodeB(t1,t2)

然后你可以达到你想要的效果:

match tree with
| [cases related to unary nodes..]
| BinaryNode(f,t1,t2) -> mkBinaryNode f t2 t1

但如果这是一个普遍需要,那么将Tree的定义改为包含味道而不是使用活动模式处理它可能是有意义的。