Created
May 15, 2021 12:52
-
-
Save inamiy/3e797a389f029169a21fc0473aa5a2c8 to your computer and use it in GitHub Desktop.
Flattened to Tree structure
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- こういうフラットな配列から木構造を作るのってどうやったらいいんだろう? | |
-- https://twitter.com/suin/status/1390626013814476800 | |
{-# LANGUAGE DeriveFunctor, UndecidableInstances, LambdaCase #-} | |
module Main where | |
import Data.Functor.Classes | |
import Data.List | |
---------------------------------------- | |
-- Q. How to change flattened structure into Tree structure? | |
inputNodes :: [NodeF a] | |
inputNodes = [ | |
NodeF [1] "title 1" [], | |
NodeF [1, 1] "title 1-1" [], | |
NodeF [1, 1, 1] "title 1-1-1" [], | |
NodeF [1, 1, 2] "title 1-1-2" [], | |
NodeF [1, 2] "title 1-2" [], | |
NodeF [2] "title 2" [] | |
] | |
outputNodes :: [Node] | |
outputNodes = [ | |
mkNode [1] "title 1" [ | |
mkNode [1] "title 1-1" [ | |
mkNode [1] "title 1-1-1" [], | |
mkNode [2] "title 1-1-2" [] | |
], | |
mkNode [2] "title 1-2" [] | |
], | |
mkNode [2] "title 2" [] | |
] | |
---------------------------------------- | |
newtype Fix f = In { out :: f (Fix f) } | |
instance Show (f (Fix f)) => Show (Fix f) where | |
showsPrec p (In f) = showsPrec p f | |
instance Eq1 f => Eq (Fix f) where | |
In a == In b = eq1 a b | |
cata :: Functor f => (f a -> a) -> Fix f -> a | |
cata g = g . fmap (cata g) . out | |
ana :: Functor f => (a -> f a) -> a -> Fix f | |
ana g = In . fmap (ana g) . g | |
---------------------------------------- | |
data NodeF a = NodeF [Int] String [a] deriving (Functor, Show, Eq) | |
instance Eq1 NodeF where | |
liftEq _ (NodeF _ _ []) (NodeF _ _ []) = True | |
liftEq _ (NodeF _ _ []) (NodeF _ _ (_:_)) = False | |
liftEq _ (NodeF _ _ (_:_)) (NodeF _ _ []) = False | |
liftEq eq (NodeF _ _ (x:xs)) (NodeF _ _ (y:ys)) = eq x y && liftEq eq xs ys | |
type Node = Fix NodeF | |
mkNode :: [Int] -> String -> [Node] -> Node | |
mkNode keys title children = In (NodeF keys title children) | |
getKeys :: Node -> [Int] | |
getKeys (In (NodeF ks _ _)) = ks | |
---------------------------------------- | |
-- Step 1: Make a nested Tree structure using Anamorphism. | |
-- Example: | |
-- coalg (NodeF [1,2,3] "title 1-2-3" []) | |
-- = NodeF [1] "" [ NodeF [2, 3] "title 1-2-3" [] ] | |
coalg :: NodeF a -> NodeF (NodeF a) | |
coalg (NodeF (k : []) title _) = NodeF [k] title [] | |
coalg (NodeF (k : ks) title _) = NodeF [k] "" [(NodeF ks title [])] | |
-- nestedInputNode :: Node | |
-- nestedInputNode = ana coalg $ NodeF [1, 1, 2] "title 1-1-2" [] | |
-- -- == NodeF [1] "" [ NodeF [1] "" [ NodeF [2] "title 1-1-2" [] ] ] | |
nestedInputNodes :: [Node] | |
nestedInputNodes = fmap (ana coalg) inputNodes | |
---------------------------------------- | |
-- Step 2: Fold `nestedInputNodes` using Catamorphism. | |
data ListF a b = Nil | Cons a b deriving (Functor, Show, Eq) | |
type NodeList = Fix (ListF Node) | |
toList' :: [a] -> Fix (ListF a) | |
toList' [] = In Nil | |
toList' (a : as) = In (Cons a (toList' as)) | |
fromList' :: Fix (ListF a) -> [a] | |
fromList' (In Nil) = [] | |
fromList' (In (Cons a list)) = a : (fromList' list) | |
reducer :: Node -> [Node] -> [Node] | |
reducer n@(In (NodeF ks title children)) ns = | |
case elemIndex ks (fmap getKeys ns) of | |
Just i -> fmap (f i) $ zip [0..] ns | |
where | |
f i (j, n@(In (NodeF ks2 title2 children2))) = | |
if i == j | |
then In (NodeF ks (title ++ title2) (maybe children2 (\x -> reducer x children2) (safeHead children))) | |
else n | |
safeHead [] = Nothing | |
safeHead (x:xs) = Just x | |
Nothing -> [n] ++ ns | |
alg :: ListF Node NodeList -> NodeList | |
alg = \case | |
Nil -> In Nil | |
Cons n list -> toList' $ reducer n (fromList' list) | |
answer :: [Node] | |
answer = fromList' (cata alg (toList' nestedInputNodes)) | |
---------------------------------------- | |
-- Step 2b: Fold `inputNodes` using Catamorphism with running Anamorphism at the same time. | |
alg' :: ListF (NodeF a) NodeList -> NodeList | |
alg' = \case | |
Nil -> In Nil | |
Cons n list -> toList' $ reducer (ana coalg n) (fromList' list) | |
answer' :: [Node] | |
answer' = fromList' (cata alg' (toList' inputNodes)) | |
---------------------------------------- | |
main :: IO () | |
main = do | |
print $ foldr reducer [] nestedInputNodes -- using foldr | |
print $ answer -- using catamorphism | |
print $ answer' -- using catamorphism | |
print $ answer == outputNodes -- True | |
print $ answer' == outputNodes -- True |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment