f# - 类型检查建模父子关系的方式?
问题描述
我有一个类型,Item
. 一个Item
可能有一个父母Item
或一个或多个孩子Item
,或者两者都没有(即,不一定有关系,关系至多只有一层)。
s上的转换Item
有时会影响父级,有时会影响到子级。所以我需要能够双向遍历。
一个天真的表示是在 上具有一个ParentID: ID option
和ChildIDs: ID list
字段Item
。但这允许非法状态。
我可以改为在of上有一个Relationships
字段,这样更好(在这种情况下确保至少有一个孩子的元组)。Item
type Relationship = ParentID of ID | ChildIDs of ID * ID list | None
ChildIDs
但是有没有办法让编译器确保双向关系的一致性?
我在想我可以完全取消 ID 上的链接,并在最顶部引入一个有区别的联合:type Item = Item of Item | Compound of Item * Item * Item list
. (同样,Compound
元组代表一个父母,至少一个孩子,也许更多的孩子。)
缺点是现在每个转换都Item
需要检查案例和处理Compound
s。(或者也许这很好,因为它迫使我考虑每次转变的连锁反应?)
优点是Item
函数可能需要接触的所有 s 始终在一个一致的捆绑包中可用。
这是“惯用语”吗?还有其他方法可以考虑吗?
解决方案
像这样的东西会起作用吗?项目是包含有效负载(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)
通过这种方式,您不必在处理中添加任何条件逻辑(在匹配之上) - 您可以处理具有已知内容的元组。
您还可以实现记录而不是元组,这将导致更大的透明度。
推荐阅读
- sql - Spark 上的 Hive 不返回聚合或连接查询的结果
- php - Laravel中带有2个外键的搜索栏
- reactjs - 使用 withRouter 时代码覆盖率返回 0%
- java - 如何修复,未使用微调器自定义适配器中的代码检查复选框?
- php - 如何在数组中找到与另一个值相比的值
- html - 如何在div中包装输入元素
- mongodb - MongoDB 枚举一个键的所有数据类型
- php - 我的 Vue.js 代码在加载时不工作
- android - 在 NestedScrollview 中带有 HasFixedSize 的 Recyclerview wrap_content 不显示项目
- php - 使用 Carbon 延迟作业调度时出现意外的“->”