Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Finger Trees in Haskell Andreas Bergmaier Technische Universität München Seminar Fortgeschrittene Konzepte der funktionalen Programmierung“ ” Sommmersemester 2015 Idee Aufbau Konkatenation Annotierung Split Anwendungen 2,3-Bäume Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen 2,3-Bäume Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen 2,3-Bäume Finger Trees in Haskell Andreas Bergmaier data Node a = Node2 a a | Node3 a a a 1 Einführung 2 2,3-Bäume data Tree a = Zero a | Succ ( Tree ( Node a )) 3 Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Node (Node (Node a))) Node (Node a) Node a a Finger Trees in Haskell Monoid Andreas Bergmaier Einführung 2,3-Bäume Finger Trees neutrales Element mempty ‘mappend‘ a = a = a ‘mappend‘ mempty assoziative Verknüpfung (a ‘mappend‘ b) ‘mappend‘ c = a ‘mappend‘ (b ‘mappend‘ c) 1 2 3 class Monoid a where mempty :: a mappend :: a -> a -> a 4 5 6 mconcat :: [ a ] -> a mconcat = foldr mappend mempty Idee Aufbau Konkatenation Annotierung Split Anwendungen Foldable Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung 1 2 3 class Foldable t where foldMap :: Monoid m = > ( a -> m ) -> t a -> m foldMap f = foldr ( mappend . f ) mempty 4 5 foldr :: ( a -> b -> b ) -> b -> t a -> b Split Anwendungen Finger Trees Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Idee Konstanter Zugriff auf Enden Amortisiert konstante cons und snoc Vom 2,3-Baum zum Finger Tree Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Vom 2,3-Baum zum Finger Tree Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Vom 2,3-Baum zum Finger Tree Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Vom 2,3-Baum zum Finger Tree Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Finger Trees in Haskell Finger Trees Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Idee Konstanter Zugriff auf Enden Annotierung Split Anwendungen Struktur 1 2 3 4 data Digit a = | | | One a Two a a Three a a a Four a a a a 5 6 7 data FingerTree a = Empty | Single a | Spine ( Digit a ) ( FingerTree ( Node a )) ( Digit a ) Finger Trees - Aufbau Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen a Finger Trees - Aufbau Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Finger Trees - Aufbau Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Finger Trees - Aufbau Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Finger Trees - Aufbau Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Finger Trees - Aufbau Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Finger Trees - Aufbau Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees 1 2 3 4 5 6 7 8 9 10 11 12 13 <| (| >) :: FingerTree a -> a -> FingerTree a Empty | > a = Single a ( Single a ) | > b = Spine ( One a ) Empty ( One b ) ( Spine l m ( One a )) | > b = Spine l m ( Two a b ) ( Spine l m ( Two a b )) | > c = Spine l m ( Three a b c ) ( Spine l m ( Three a b c )) | > d = Spine l m ( Four a b c d ) ( Spine l m ( Four a b c d )) | > e = Spine l ( m | > Node3 a b c ) ( Two d e ) symmetrisch zu |> Idee Aufbau Konkatenation Annotierung Split Anwendungen Finger Trees - Aufbau Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation 1 2 3 ( < <|) :: Foldable f = > f a -> FingerTree a -> FingerTree a ( < <|) = flip $ foldr ( <|) 4 5 6 7 (| > >) :: Foldable f = > FingerTree a -> f a -> FingerTree a (| > >) = foldl (| >) 8 9 fromList = ( < <| Empty ) Annotierung Split Anwendungen Finger Trees - Konkatenation Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume 1 2 3 4 5 6 7 8 9 10 11 12 app :: FingerTree a -> [ a ] -> FingerTree a -> FingerTree a app Empty ns rs = ns < <| rs app ls ns Empty = ls | > > ns app ( Single l ) ns rs = l <| ( ns < <| rs ) app ls ns ( Single r ) = ( ls | > > ns ) | > r app ( Spine prl tl sfl ) ns ( Spine prr tr sfr ) = Spine prl ( app tl ( nodes $ toList sfl ++ ns ++ toList prr ) tr ) sfr 13 14 15 16 17 18 nodes nodes nodes nodes nodes :: [ a ] -> [ Node a ] ( a : b :[]) = Node2 ( a : b : c :[]) = Node3 ( a : b : c : d :[]) = Node2 ( a : b : c : ns ) = Node3 a a a a b : [] b c : [] b : Node2 c d : [] b c : nodes ns Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen Finger Trees - Konkatenation Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen 1 2 3 instance Monoid ( FingerTree a ) where mempty = Empty mappend a b = app a [] b Annotierung Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Struktur 1 data Node m a = Node2 ! m a a | Node3 ! m a a a 2 3 4 5 6 7 data FingerTree m a = Empty | Single ! m a | Spine ! m !( Digit a ) ( FingerTree m ( Node m a )) !( Digit a ) Annotierung Split Anwendungen Annotierung Finger Trees in Haskell Andreas Bergmaier Idee Einführung 2,3-Bäume Finger Trees 1 2 class Monoid m = > Measured v m where measure :: v -> m Idee Aufbau Konkatenation Annotierung Split Anwendungen Beispiele 1 2 3 instance Monoid m = > Measured ( Node m a ) m where measure ( Node2 m _ _ ) = m measure ( Node3 m _ _ _ ) = m 4 5 6 7 8 instance Monoid m = > Measured ( FingerTree m a ) m where measure Empty = mempty measure ( Single m _ ) = m measure ( Spine m _ _ _ ) = m 9 10 11 instance Measured a m = > Measured ( Digit a ) m where measure = mconcat . map measure . toList Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees 1 2 3 4 5 6 7 8 9 10 11 12 split :: Measured a m = > ( m -> Bool ) -> FingerTree m a -> Maybe ( FingerTree m a , a , FingerTree m a ) split _ Empty = Nothing split pred t | pred mempty = Nothing | not ( pred mall ) = Nothing | otherwise = Just $ splitFrom pred mempty t where mall = measure t Idee Aufbau Konkatenation Annotierung Split Anwendungen 1 2 3 4 5 6 7 8 9 splitList :: Measured a m = > ( m -> Bool ) -> m -> [ a ] -> ([ a ] , a , [ a ]) splitList pred _ [x] = ([] , x , []) splitList pred acc ( x : xs ) = if pred acc ’ then ([] , x , xs ) else ( x : xs ’ , x ’ , rs ’) where acc ’ = acc ‘ mappend ‘ measure x ( xs ’ , x ’ , rs ’) = splitList acc ’ xs Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 splitNode :: Measured a m = > ( m -> Bool ) -> m -> Node m a -> ([ a ] , a , [ a ]) splitNode pred acc ( Node2 _ a b ) = let aacc = acc ‘ mappend ‘ measure a in if pred aacc then ([] , a , [ b ]) else ([ a ] , b , []) splitNode pred acc ( Node3 _ a b c ) = let aacc = acc ‘ mappend ‘ measure a bacc = aacc ‘ mappend ‘ measure b in if pred aacc then ([] , a , [b , c ]) else if pred bacc then ([ a ] , b , [ c ]) else ([ a , b ] , c , []) Finger Trees in Haskell 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Andreas Bergmaier splitFrom :: Measured a m = > ( m -> Bool ) -> m -> FingerTree m a Einführung -> ( FingerTree m a , a , FingerTree m a ) 2,3-Bäume splitFrom pred acc ( Single _ x ) = ( Empty , x , Empty ) Finger Trees splitFrom pred acc ( Spine _ pr t sf ) Idee Aufbau | pred pracc = let ( ls , x , rs ) = Konkatenation splitFoldable pred acc pr Annotierung in ( fromList ls , x , spineL rs t sf ) Split | pred tacc = let ( lt , ts , rt ) = Anwendungen splitFrom pred pracc t prlacc = pracc ‘ mappend ‘ measure lt ( ls , x , rs ) = splitFoldable pred prlacc ts in ( spineR pr lt ls , x, spineL rs rt sf ) | otherwise = let ( ls , x , rs ) = splitFoldable pred tacc sf in ( spineR pr t ls , x , fromList rs ) where pracc = acc ‘ mappend ‘ measure pr tacc = pracc ‘ mappend ‘ measure t Anwendungen Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume Finger Trees Beispiele Idee Aufbau Konkatenation Annotierung Indexed Sequence Ordered Sequence Priority Queue Interval Tree 1 newtype Elem a = Elem { getElem :: a } 2 3 instance Measured ( Elem a ) ... where Split Anwendungen Indexed Sequence Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume 1 2 3 4 5 6 1 2 −− from Data . Monoid −− Monoid under addition newtype Sum a = Sum { getSum :: a } instance Num a = > Monoid ( Sum a ) where mempty = Sum 0 Sum x ‘ mappend ‘ Sum y = Sum ( x + y ) instance Measured ( Elem a ) ( Sum Int ) where measure ( Elem _ ) = Sum 1 3 4 5 6 at :: Int -> FingerTree ( Sum Int ) ( Elem a ) -> Maybe a at i t = fmap (\( _ ,x , _ ) - > getElem x ) ( split (( > i ) . getSum ) t ) 7 8 9 length :: FingerTree ( Sum Int ) ( Elem a ) -> Int length = getSum . measure Finger Trees Idee Aufbau Konkatenation Annotierung Split Anwendungen MaxPriority Queue Finger Trees in Haskell Andreas Bergmaier Einführung 2,3-Bäume 1 data Prio a = NegInfinity | Prio a deriving ( Eq , Ord , Show Finger ) Trees 2 3 4 5 6 7 1 2 instance ( Ord a ) = > Monoid ( Prio a ) where mempty = NegInfinity mappend NegInfinity x = x mappend x NegInfinity = x mappend ( Prio a ) ( Prio b ) = Prio ( a ‘max ‘ b ) instance Ord a = > Measured ( Elem a ) ( Prio a ) where measure ( Elem x ) = Prio x 3 4 5 6 7 8 9 extract :: Ord a = > FingerTree ( Prio a ) ( Elem a ) -> Maybe (a , FingerTree ( Prio a ) ( Elem a )) extract t = fmap (\( l ,x , r ) -> ( getElem x , mappend l r )) ( split ( >= max ) t ) where max = measure t Idee Aufbau Konkatenation Annotierung Split Anwendungen