Можно ли выполнить поиск на графике, построенном по стратегии связывания узлов?

Стратегия привязки к узлу может быть использована для построения графиков, например, используя простой двусторонний граф в качестве примера:

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

3 ответа

Решение

Вам действительно нужна какая-то маркировка, потому что внутри 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, определены равными выражениями, и реализовать каждую пару как один базовый объект. (Это называется устранением общего выражения выражений.)

На самом деле, было бы даже законно идентифицировать все четыре, хотя я сомневаюсь, что компиляторы действительно делают это, так как это потребовало бы заметить, что они имеют точно такое же семантическое "обозначение", поскольку все это по сути разные способы записи бесконечного дерева t = Node t t = Node (Node ... ...) (Node ... ...) - с точки зрения денотационной семантики, это единственное значение вашего типа данных, которое не содержит оснований.

В общем, вы должны иметь возможность сравнивать узел на равенство с ранее наблюдаемыми узлами, чтобы определить, действительно ли вы повторно посещаете часть графика, а не опустились в подграф схожей структуры. Это независимо от того, как вы синтаксически выражаете свои узлы или на каком языке.

Например, используя одно из представленных представлений, невозможно различить граф

a - b
|   |
c - d

от

a - b
| /
c

или же

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

или даже

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

Каждое локальное наблюдение - это узел с двумя ребрами неотличимых объектов.

Если вы добавите идентификатор, такой как int, к краям или узлам, или вы обманываете и крадете идентификатор (например, адрес, но в Haskell это не является детерминированным из-за GC), вы можете использовать эта информация выводит равенство или неравенство.

Вы можете наблюдать участие в 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
Другие вопросы по тегам