Ниже представлена простая программа Haskell, которая вычисляет равенства на деревьях:
import Control.Monad
import Control.Applicative
import Data.Maybe
data Tree = Leaf | Node Tree Tree
eqTree :: Tree -> Tree -> Maybe ()
eqTree Leaf Leaf = return ()
eqTree (Node l1 r1) (Node l2 r2) = eqTree l1 l2 >> eqTree r1 r2
eqTree _ _ = empty
Предположим, что у вас есть список ассоциаций деревьев [(Tree, a)]
, и вы хотите найти запись для данного дерева. (Можно подумать об этом как о упрощенной версии проблемы поиска экземпляров класса типа). Наивно нам нужно было бы работать O (n * s), где n - количество деревьев, а s - размер каждого дерева.
Мы можем сделать лучше, если мы используем trie-карту для представления нашего списка ассоциаций:
(>.>) = flip (.)
data TreeMap a
= TreeMap {
tm_leaf :: Maybe a,
tm_node :: TreeMap (TreeMap a)
}
lookupTreeMap :: Tree -> TreeMap a -> Maybe a
lookupTreeMap Leaf = tm_leaf
lookupTreeMap (Node l r) = tm_node >.> lookupTreeMap l >=> lookupTreeMap r
Наш поиск теперь занимает только O (s). Этот алгоритм является строгим обобщением предыдущего, поскольку мы можем проверить равенство, создав singleton TreeMap ()
, а затем посмотрим, вернемся ли мы назад Just ()
. Но по практическим соображениям мы бы предпочли не делать этого, так как это связано с созданием TreeMap, а затем сразу же срывает его.
Есть ли способ обобщить две части кода выше на новую функцию, которая может работать как на Tree
, так и на TreeMap
? Кажется, что существует некоторое сходство в том, как структурирован код, но неясно, как отвлечь различия.