2016-11-28 8 views
3

Давайте есть определение типа для дерева с несколькими типами бинарных узлов, среди других типов узлов, т.е.Подмножества членов Союза, как «параметр» в шаблоне сопоставления

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 отдельно. Это может иметь практическое значение, если существует большое количество ароматов бинарных узлов. Дерево типов - AST для Fsacc/Fslex-сгенерированного парсера/lexer, что ограничивает возможности его реструктуризации. Есть идеи?

+5

Зачем вам нужны несколько конструкторов для 'BinaryNode'? Не могли бы вы изменить его на один конструктор с дополнительным элементом 'Tag', который кодирует информацию, например. 'BinaryNode тега * Tree * Tree' и' type Tag = A | B'? – Lee

ответ

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, чтобы включить вкус вместо того, чтобы иметь дело с ним, используя активные шаблоны.

+0

Основываясь на ответе, F # не включает точный вид подхода, который я искал. Поэтому я собираюсь изменить дерево, указав тип Flavor и имея один член объединения BinaryNode с определением Flavor. Этого вполне достаточно для этого приложения, но я все еще чувствую, что язык F # может быть более гибким. – Serendip