结合策略可用于构建图形,例如,使用简单的双边图形作为示例:
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
答案 0 :(得分:4)
通常,您必须能够将节点与先前观察到的节点进行相等性比较,以确定您实际上正在重新访问图形的一部分而不是降级为具有类似结构的子图。这与您在语法上如何表达节点或使用何种语言无关。
例如,使用提供的表示法无法区分
图表a - b
| |
c - d
这
a - b
| /
c
或
a - b - c
| |
d - e - f
甚至
a - b a -
| | or heck even | |
- - - --
每个局部观察都是一个节点,有两个边缘与无法区分的实体。
如果要么向边缘或节点添加标识符(例如int),要么欺骗并窃取标识符(例如地址,但在Haskell中这不是确定性因为GC)那么您可能是能够使用这些信息来推断平等或不平等。
答案 1 :(得分:4)
你确实需要某种标记,因为从Haskell内部无法区分所写的节点。实际上,当Haskell编译器看到
时square = a where
a = Node b c
b = Node a d
c = Node a d
d = Node b c
完全是合法的,因为它注意到a
和d
以及b
和c
是由相等的人定义的表达式,并将每个对实现为一个底层对象。 (这称为常见的消除Subexpression。)
事实上,识别所有四个都是合法的,尽管我怀疑编译器确实这样做,因为它需要注意到它们具有完全相同的语义"表示",所有这些本质上都是不同的编写无限树t = Node t t = Node (Node ... ...) (Node ... ...)
的方法 - 从指称语义的角度来看,它是你的数据类型的唯一值,它不包含底部。
答案 2 :(得分:1)
您可以使用以下方式观察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