是否可以对使用打结策略构建的图进行搜索?

Is it possible to do a search on a graph constructed with the tying-the-knot strategy?

打结策略可用于构建图,例如,以简单的双边图为例:

data Node = Node Node Node

-- a - b
-- |   |
-- c - d
square = a where
    a = Node b c
    b = Node a d
    c = Node a d
    d = Node b c

该策略相当优雅,但如果没有 Int 标签,我找不到实际使用它的方法。例如,我如何编写一个函数来计算 square 值上的节点数量?

countNodes :: Node -> Int
countNodes = ... ??? ...

main = print $ countNodes square
-- output: 4

一般来说,您必须能够比较一个节点与之前观察到的节点是否相等,以确定您实际上是在重新访问图的一部分,而不是下降到具有相似结构的子图中。这与您在句法上如何表达节点或使用何种语言无关。

例如,使用提供的任何一种表示都无法区分

的图形
a - b
|   |
c - d

来自

a - b
| /
c

a - b - c
|       |
d - e - f

甚至

a - b                 a -
|   |   or heck even  |  |
- - -                  --

每个局部观察都是一个节点,具有两条边,无法区分实体。

如果您向边或节点添加标识符(例如 int),或者您欺骗并窃取标识符(例如地址,但在 Haskell 中这不是确定性的,因为GC) 那么您也许可以使用此信息来推断相等或不相等。

您确实需要某种标记,因为从内部 Haskell 无法区分写入的节点。事实上,当 Haskell 编译器看到

square = a where
    a = Node b c
    b = Node a d
    c = Node a d
    d = Node b c

完全 合法地注意到 ad,以及 bc , 由相等的表达式定义,并将每一对实现为 one 底层对象。 (这称为公共子表达式消除。)

事实上,它甚至可以识别所有四个,尽管我怀疑编译器是否真的这样做,因为它需要注意它们具有完全相同的语义 "denotation",都是本质上不同的方式编写无限树 t = Node t t = Node (Node ... ...) (Node ... ...) - 从指称语义的角度来看,这是不包含底部的数据类型的 唯一 值。

您可以观察 IO 中的分享,例如使用data-reify:

{-# LANGUAGE TypeFamilies #-}
import Data.Reify

data Node = Node Node Node
data NodeId s = NodeId s s

instance MuRef Node where
    type DeRef Node = NodeId
    mapDeRef f (Node n1 n2) = NodeId <$> f n1 <*> f n2

countNodes 的实现现在很简单(但请注意它在 IO 中!)

countNodes :: Node -> IO Int
countNodes n = do
    Graph nodes root <- reifyGraph n
    return $ length nodes

你的例子:

square :: Node
square = a where
    a = Node b c
    b = Node a d
    c = Node a d
    d = Node b c

*Main> print =<< countNodes square
4