I'm experimenting how a Relation (a cartesian product's subset) can be implemented in Haskell using different data classes.
Forest and Tree allow to keeps together in a single data structure different relations each one of different n-arity.
This set of functions require only the Eq constraint and a simple Normal Form. (The two show functions require the additional Show constraint.)
The examples allow to quick test the functions.
module RelationT
(isTreeInNormalForm
,isForestInNormalForm
,toTreeNormalForm
,toForestNormalForm
,equTrees
,equForests
,unionOfTwoTrees
,unionOfTwoForests
,unionOfForests
,addTreeToTree
,addForestToTree
,addForestToForest
,addTreeToForest
,appendTreeToTree
,appendForestToTree
,appendForestToForest
,growthTree
,growthForest
,fmapLeafsOfTree
,fmapLeafsOfForest
,showF
,showT) where
import Data.Tree
import Data.List
-- -------------------- CAVEAT --------------------
-- Forests and subforests must be in NORMAL FORM.
-- (Node values of th same level must all be different.)
-- ============================== NORMAL FORM ==============================
-- Each tree and forest must result True.
isTreeInNormalForm :: Eq a => Tree a -> Bool
isTreeInNormalForm (Node _ subforest) = isForestInNormalForm subforest
isForestInNormalForm :: Eq a => Forest a -> Bool
isForestInNormalForm [] = True
isForestInNormalForm [Node _ subfrst] = isForestInNormalForm subfrst
isForestInNormalForm (Node x _ : ndxs) =
let (oks,others) = partition (\(Node w _) -> w == x) ndxs
in case oks of
[] -> isForestInNormalForm others
_ -> False
-- "Fusion" of the nodes in the same level that have the same value
toTreeNormalForm :: Eq a => Tree a -> Tree a
toTreeNormalForm (Node x subforest) = Node x $ toForestNormalForm subforest
toForestNormalForm :: Eq a => Forest a -> Forest a
toForestNormalForm [] = []
toForestNormalForm [Node x subfrst] = [Node x (toForestNormalForm subfrst)]
toForestNormalForm (Node x subfrst : ndxs) =
let (oks,others) = partition (\(Node w _) -> w == x) ndxs
in if null oks
then Node x (toForestNormalForm subfrst) : toForestNormalForm others
else Node x (unionOfForests $ map (\(Node _ subf) -> subf) oks) : toForestNormalForm others
-- ============================== EQUIVALENCE ==============================
-- Forests and subforests are CONSIDERED equivalent if differs only by the order of nodes in the same level.
equTrees :: Eq a => Tree a -> Tree a -> Bool
equTrees (Node x subfrstX) (Node y subfrstY)
| x == y = equForests subfrstX subfrstY
| otherwise = False
equForests :: Eq a => Forest a -> Forest a -> Bool
equForests [] [] = True
equForests [] _ = False
equForests _ [] = False
equForests (Node x subfrstX : ndxs) ndys =
let (oks,others) = partition (\(Node w _) -> w == x) ndys
in case oks of
[] -> False
[Node _ subfrstZ] -> equForests subfrstX subfrstZ && equForests ndxs others
_ -> error "NOT in Normal Form"
-- ============================== COMBINE ==============================
unionOfTwoTrees :: Eq a => Tree a -> Tree a -> [Tree a]
unionOfTwoTrees nd (Node _ []) = [nd]
unionOfTwoTrees (Node _ []) nd = [nd]
unionOfTwoTrees ndx@(Node x subforestX) ndy@(Node y subforesty)
| x == y = [Node x (unionOfTwoForests subforestX subforesty)]
| otherwise = [ndx, ndy]
unionOfTwoForests :: Eq a => [Tree a] -> [Tree a] -> [Tree a]
unionOfTwoForests [] frst = frst
unionOfTwoForests frst [] = frst
unionOfTwoForests (ndy@(Node y subfrstY) : ndys) ndxs =
let (oks,others) = partition (\(Node w _) -> w == y) ndxs
in case oks of
[] -> ndy : unionOfTwoForests ndys others
[Node z subfrstZ] -> Node z (unionOfTwoForests subfrstY subfrstZ) : unionOfTwoForests ndys others
_ -> error "NOT in Normal Form"
unionOfForests :: (Foldable t, Eq a) => t [Tree a] -> [Tree a]
unionOfForests frsts = foldr unionOfTwoForests [] frsts
-- -------------------- Addition --------------------
-- Addition is non-commutative.
-- The first tree is "added" to the second only if the "root" values are the same.
-- The following functions behaves similarly.
addTreeToTree :: Eq a => Tree a -> Tree a -> Tree a
addTreeToTree (Node _ []) nd = nd
addTreeToTree (Node y subfrstY) ndx@(Node x [])
| y == x = Node x subfrstY
| otherwise = ndx
addTreeToTree (Node y subfrstY) ndx@(Node x subfrstX)
| y == x = Node x $ addForestToForest subfrstY subfrstX
| otherwise = ndx
addTreeToForest :: Eq a => Tree a -> [Tree a] -> [Tree a]
addTreeToForest (Node _ []) frst = frst
addTreeToForest _ [] = []
addTreeToForest ndy@(Node y _) frst =
let (oks,others) = partition (\(Node w _) -> w == y) frst
in case oks of
[] -> frst
[ndz] -> addTreeToTree ndz ndy : others
_ -> error "The forest is NOT in Normal Form"
addForestToTree :: Eq a => [Tree a] -> Tree a -> Tree a
addForestToTree [] nd = nd
addForestToTree ndys nd@(Node x subforestX) =
case filter (\(Node w _) -> w == x) ndys of
[] -> nd
[Node _ subfrstZ] -> Node x $ appendForestToForest subfrstZ subforestX
_ -> error "The forest is NOT in Normal Form"
addForestToForest :: Eq a => [Tree a] -> [Tree a] -> [Tree a]
addForestToForest [] frst = frst
addForestToForest _ [] = [] -- !!!
addForestToForest (Node y subfrstY : ndys) ndxs =
let (oks,others) = partition (\(Node w _) -> w == y) ndxs
in case oks of
[] -> addForestToForest ndys ndxs
[Node z subfrstZ] -> Node z (unionOfTwoForests subfrstY subfrstZ) : addForestToForest ndys others
_ -> error "The second forest is NOT in Normal Form"
-- -------------------- Append --------------------
-- Thees functions appends trees or forests to the leaves of the target forest.
-- For each target leaf the condition is that the value of the leaf have to be equal to the value of the "root" of what would be appended.
appendTreeToTree :: Eq a => Tree a -> Tree a -> Tree a
appendTreeToTree (Node _ []) nd = nd
appendTreeToTree (Node y subfrstY) ndx@(Node x [])
| y == x = Node x subfrstY
| otherwise = ndx
appendTreeToTree ndy (Node x subfrstX) = Node x $ map (\nd -> appendTreeToTree ndy nd) subfrstX
appendForestToTree :: Eq a => [Tree a] -> Tree a -> Tree a
appendForestToTree [] nd = nd
appendForestToTree frst ndx@(Node x []) =
case filter (\(Node y _) -> y == x) frst of
[] -> ndx
[Node _ subforest] -> Node x subforest
_ -> error "The forest is NOT in Normal Form"
appendForestToTree frst (Node x subforestX) = Node x $ appendForestToForest frst subforestX
appendForestToForest :: Eq a => [Tree a] -> [Tree a] -> [Tree a]
appendForestToForest [] frst = frst
appendForestToForest frst [] = frst
appendForestToForest frstY frstX = map (\ndx -> appendForestToTree frstY ndx) frstX
-- ============================== CONSTRUCTION ==============================
-- These functions can selectively add nodes to the leaves of the target.
growthTree :: (t -> Forest t) -> Tree t -> Tree t
growthTree f (Node x []) = Node x (f x)
growthTree f (Node x subfrst) = Node x $ growthForest f subfrst
growthForest :: (t -> Forest t) -> [Tree t] -> [Tree t]
growthForest f [] = []
growthForest f frst = map (growthTree f) frst
-- ============================== LEAF'S VALUE MODIFICATION ==============================
-- Thees functions nodify only the leaves of the target.
fmapLeafsOfTree :: (a -> a) -> Tree a -> Tree a
fmapLeafsOfTree f (Node x []) = Node (f x) []
fmapLeafsOfTree f (Node x subfrst) = Node x $ fmapLeafsOfForest f subfrst
fmapLeafsOfForest :: (a -> a) -> Forest a -> Forest a
fmapLeafsOfForest f [] = []
fmapLeafsOfForest f frst = map (fmapLeafsOfTree f) frst
-- ============================== PRETTY PRINT ==============================
-- Functions that are useful for values other than strings.
showF :: Show a => [Tree a] -> IO ()
showF = putStr . drawForest . map (fmap show)
showT :: Show a => Tree a -> IO ()
showT = putStr . drawTree . fmap show
-- ============================== EXAMPLES ==============================
tr1 = Node "John" [Node "friends" [Node "John" [], Node "Sally"[]]
,Node "cars" [Node "Laputa" [], Node "Pinto"[]]
,Node "motorbikes" [Node "Thunderbird"[]]]
tr2 = Node "Mary" [Node "motorbikes" [Node"Varadero" []]
,Node "past holidays" [Node "Rome" [],Node "Sydney" []]]
tr3 = Node "John" [Node "motorbikes" [Node"Vespa" []]
,Node "past holidays" [Node "Rome" [],Node "Paris" []]]
tr4 = Node "John" [Node "friends" [Node "Mary" [], Node "Rick"[]]
,Node "past holidays" [Node "Rome" [], Node "London" []]]
tr5 = Node "Frank" [Node "home" [Node "New York" []]]
tr6 = Node "Sally" [Node "phone numbers" [Node "345-25-33" [], Node "898-38-234" []]]
tr7 = Node "Rose" [Node "phone numbers" [Node "525-33-425" []]]
tr8 = Node "Thunderbird" [Node "manufacturing company" [Node "Triumph Engineering"[]]]
tr9 = Node "Varadero" [Node "manufacturing company" [Node "Honda" []]]
fr1 = [tr1, tr2]
fr2 = [tr4, tr5]
fr3 = [tr6, tr7]
fr4 = [tr8,tr9]
-------------------------
ex1 = showF $ unionOfTwoForests fr1 fr2
ex2 = showF $ unionOfTwoForests fr1 fr2
ex3 = showF $ unionOfForests [fr1,fr2,fr3,fr4]
ex4 = showF $ addForestToForest fr2 fr1
ex5 = showF $ addForestToForest fr1 fr2
ex6 = showF $ appendForestToForest fr4 fr1
ex7 = showF $ appendForestToForest fr3 fr1
ex10 = showF $ appendForestToForest (unionOfForests [fr3,fr4]) (unionOfForests [fr1,fr2])
ex11 = showF $ appendForestToForest (unionOfForests [fr3,fr4]) (unionOfForests [fr1,fr2,fr3,fr4])
countryOf x = case x of
"Rome" -> [Node "country" [Node "Italy" []]]
"Paris" -> [Node "country" [Node "France" []]]
_ -> []
ex20 = showT $ growthTree countryOf tr3