首页 > 解决方案 > 类型检查建模父子关系的方式?

问题描述

我有一个类型,Item. 一个Item可能有一个父母Item 一个或多个孩子Item或者两者都没有(即,不一定有关系,关系至多只有一层)。

s上的转换Item有时会影响父级,有时会影响到子级。所以我需要能够双向遍历。

一个天真的表示是在 上具有一个ParentID: ID optionChildIDs: ID list字段Item。但这允许非法状态。

我可以改为在of上有一个Relationships字段,这样更好(在这种情况下确保至少有一个孩子的元组)。Itemtype Relationship = ParentID of ID | ChildIDs of ID * ID list | NoneChildIDs

但是有没有办法让编译器确保双向关系的一致性?

我在想我可以完全取消 ID 上的链接,并在最顶部引入一个有区别的联合:type Item = Item of Item | Compound of Item * Item * Item list. (同样,Compound元组代表一个父母,至少一个孩子,也许更多的孩子。)

缺点是现在每个转换都Item需要检查案例和处理Compounds。(或者也许这很好,因为它迫使我考虑每次转变的连锁反应?)

优点是Item函数可能需要接触的所有 s 始终在一个一致的捆绑包中可用。

这是“惯用语”吗?还有其他方法可以考虑吗?

标签: f#

解决方案


像这样的东西会起作用吗?项目是包含有效负载(ID 和数据属性)的记录。

有区别的联合定义了你的四个场景。

type Item = {ID: int; Name:string} //whatever is required

type ItemNode =
| StandaloneItem of Item
| ItemWithParent of Item * Item
| ItemWithChilren of Item * Item list
| ItemWithParentAndChildren of Item * Item * Item list

let processItem (item:Item) = 42

然后你可以实现类似这样的节点处理:

let processNode (item:ItemNode) = 
    match item with
    | StandaloneItem it -> it |> processItem 
    | ItemWithParent (parent, it) -> [parent; it] |> List.map processItem |> List.sum
    | ItemWithChilren (it, children) -> (it |> processItem) + (children |> List.map processItem |> List.sum)
    | ItemWithParentAndChildren (it, parent, children) -> (it |> processItem) 
                                                          + (parent |> processItem) 
                                                          + (children |> List.map processItem |> List.sum)

通过这种方式,您不必在处理中添加任何条件逻辑(在匹配之上) - 您可以处理具有已知内容的元组。

您还可以实现记录而不是元组,这将导致更大的透明度。


推荐阅读