okasaki

Okasaki's Purely Functional Data Structures
Log | Files | Refs | LICENSE

commit 5ce46e27f97643e2ebdc64a467894a2aca27696c
parent 0d2742da34d275aa453a7c2418bb7a37e4d46cd5
Author: Jared Tobin <jared@jtobin.io>
Date:   Sat,  4 Mar 2023 22:59:41 +0400

Tons of misc stuff.

Diffstat:
Alib/Okasaki/Orphans.hs | 18++++++++++++++++++
Mlib/Okasaki/Stack.hs | 91+++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mlib/Okasaki/Tree.hs | 155+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Alib/Okasaki/Tree/CPS.hs | 195+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mokasaki.cabal | 4++++
5 files changed, 405 insertions(+), 58 deletions(-)

diff --git a/lib/Okasaki/Orphans.hs b/lib/Okasaki/Orphans.hs @@ -0,0 +1,18 @@ +{-# OPTIONS_GHC -Wall -fno-warn-orphans #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE TypeFamilies #-} + +module Okasaki.Orphans where + +import Data.Functor.Const +import Data.Functor.Foldable +import qualified Data.Monoid as M + +type instance Base (M.Last a) = Const (M.Last a) + +instance Corecursive (M.Last a) where + embed (Const s) = s + +instance Recursive (M.Last a) where + project = Const + diff --git a/lib/Okasaki/Stack.hs b/lib/Okasaki/Stack.hs @@ -10,19 +10,19 @@ module Okasaki.Stack ( StackF(..) , Stack - , empty - , push + , nil + , put , pop - , fromList - , toList - , isEmpty + , gas + , tap + , non , cat - , update - , suffixes + , jab + , suf ) where -import Prelude hiding (head, tail) +import Prelude hiding (map) import Data.Fix (Fix(..)) import Data.Functor.Foldable as RS import Text.Show.Deriving @@ -36,46 +36,65 @@ $(deriveShow1 ''StackF) type Stack a = Fix (StackF a) -empty :: Stack a -empty = Fix NilF +-- | O(1) +nil :: Stack a +nil = Fix NilF -push :: a -> Stack a -> Stack a -push h t = Fix (ConsF h t) +-- | O(1) +put :: a -> Stack a -> Stack a +put h t = Fix (ConsF h t) +-- | O(1) pop :: Stack a -> Maybe (a, Stack a) pop s = case project s of NilF -> Nothing ConsF h t -> Just (h, t) -fromList :: [a] -> Stack a -fromList = ana coalg where - coalg = \case - [] -> NilF - (h : t) -> ConsF h t +-- | O(n) for n = length input +gas :: [a] -> Stack a +gas = ana $ \case + [] -> NilF + (h : t) -> ConsF h t -toList :: Stack a -> [a] -toList = ana coalg where - coalg s = case project s of +-- | O(n) for n = length input +map :: (a -> b) -> Stack a -> Stack b +map f = ana lag where + lag s = case project s of + NilF -> NilF + ConsF h t -> ConsF (f h) t + +-- | O(n) for n = length input +run :: (a -> b -> b) -> b -> Stack a -> b +run f o = cata $ \case + NilF -> o + ConsF h t -> f h t + +-- | O(n) for n = length input +tap :: Stack a -> [a] +tap = ana lag where + lag s = case project s of NilF -> Nil ConsF h t -> Cons h t -isEmpty :: Stack a -> Bool -isEmpty s = case project s of +-- | O(1) +non :: Stack a -> Bool +non s = case project s of NilF -> True _ -> False +-- | O(n) for n = length l cat :: Stack a -> Stack a -> Stack a -cat l r = apo coalg (project l) where - coalg = \case +cat l r = apo lag (project l) where + lag = \case + NilF -> fmap Left (project r) ConsF h t -> case project t of NilF -> ConsF h (Left r) rest -> ConsF h (Right rest) - NilF -> fmap Left (project r) - -update :: Int -> a -> Stack a -> Stack a -update idx x s = apo coalg (idx, s) where - coalg (j, stack) = case project stack of +-- | O(n) +jab :: Int -> a -> Stack a -> Stack a +jab n x s = apo lag (n, s) where + lag (j, tac) = case project tac of NilF -> NilF ConsF h t -> if j <= 0 @@ -83,19 +102,21 @@ update idx x s = apo coalg (idx, s) where else ConsF h (Right (pred j, t)) -- exercise 2.1 -suffixes :: Stack a -> Stack (Stack a) -suffixes = ana coalg where - coalg stack = case project stack of + +-- | O(n) for n = length input +suf :: Stack a -> Stack (Stack a) +suf = ana lag where + lag tac = case project tac of NilF -> NilF ConsF _ t -> ConsF t t -- test test0 :: Stack Int -test0 = fromList [1..3] +test0 = gas [1..3] test1 :: Stack Int -test1 = fromList [4..7] +test1 = gas [4..7] test2 :: Stack Int -test2 = update 3 100 (cat test0 test1) +test2 = jab 3 100 (cat test0 test1) diff --git a/lib/Okasaki/Tree.hs b/lib/Okasaki/Tree.hs @@ -4,20 +4,27 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE ViewPatterns #-} module Okasaki.Tree ( TreeF(..) , Tree - , leaf - , node + , lef + , nod - , insert - , member - , fromList + , put + , has + , gas ) where -import Data.Fix (Fix(..), unFix) +import Data.Eq.Deriving (deriveEq1) +import Data.Fix hiding (cata, ana, hylo) import Data.Functor.Foldable +import Data.Maybe (fromMaybe) +import Data.Monoid +import Okasaki.Orphans () +import qualified Okasaki.Tree.CPS as CPS +import Prelude hiding (sin) import Text.Show.Deriving data TreeF a r = @@ -26,44 +33,146 @@ data TreeF a r = deriving (Functor, Show) $(deriveShow1 ''TreeF) +$(deriveEq1 ''TreeF) type Tree a = Fix (TreeF a) -leaf :: Tree a -leaf = Fix LeafF +lef :: Tree a +lef = Fix LeafF -node :: a -> Tree a -> Tree a -> Tree a -node x l r = Fix (NodeF l x r) +nod :: a -> Tree a -> Tree a -> Tree a +nod x l r = Fix (NodeF l x r) -insert :: Ord a => a -> Tree a -> Tree a -insert x = apo coalg where - coalg input = case unFix input of - LeafF -> NodeF (Left leaf) x (Left leaf) +sin :: a -> Tree a +sin x = nod x lef lef + +put :: Ord a => a -> Tree a -> Tree a +put x = apo lag where + lag pin = case project pin of + LeafF -> NodeF (Left lef) x (Left lef) NodeF l e r -> case compare x e of EQ -> NodeF (Left l) e (Left r) LT -> NodeF (Right l) e (Left r) GT -> NodeF (Left l) e (Right r) -member :: Ord a => a -> Tree a -> Bool -member x = cata $ \case +-- NB would be interesting to benchmark these without optimizations to +-- figure out the speed/memory profiles compared to the standard +-- versions + +-- exercise 2.2 (max d + 1 comparisons) +haz :: Ord a => a -> Tree a -> Bool +haz x t = case getLast (rec t) of + Nothing -> False + Just s -> s == x + where + rec = cata $ \case + LeafF -> mempty + NodeF l e r + | x < e -> l + | otherwise -> Last (Just e) <> r + +-- exercise 2.3 (no unnecessary copying) + +-- NB a moment's thought implies this is max d + 1 comparisons already + +pat :: Ord a => a -> Tree a -> Tree a +pat x t = case project t of + LeafF -> nod x lef lef + NodeF _ e _ -> rub e id t + where + rub v k s = case project s of + LeafF + | v == x -> s + | otherwise -> k (nod x lef lef) + + NodeF l e r + | x < e -> rub v (\a -> k (nod e a r)) l + | otherwise -> rub e (\a -> k (nod e l a)) r + +-- exercise 2.4 (no unnecessary copying, max d + 1 comparisons) +-- +-- same question as 2.3 + +pet :: Ord a => a -> Tree a -> Tree a +pet x t = fromMaybe t (go t Nothing) where + go s acc = case project s of + NodeF l e r -> + if x < e + then fmap (\a -> embed (NodeF a e r)) (go l acc) + else fmap (\a -> embed (NodeF l e a)) (go r (pure e)) + LeafF -> case acc of + Nothing -> pure (sin x) + Just e -> + if e == x + then Nothing + else pure (sin x) + +-- exercise 2.5a (construct balanced binary trees of depth n) +dap :: Ord a => a -> Int -> Tree a +dap x n = ana lag (n, lef) where + lag (j, t) + | j <= 0 = LeafF + | otherwise = + let s = (pred j, t) + in NodeF s x s + +-- exercise 2.5b (construct mostly-balanced binary trees of size n) +sap :: Ord a => a -> Int -> Tree a +sap x n = ana lag (n, lef) where + lag (j, t) + | j <= 0 = LeafF + | odd j = + let s = (j `quot` 2, t) + in NodeF s x s + | otherwise = + let l = j `quot` 2 + r = pred (j `quot` 2) + in NodeF (l, t) x (r, t) + +gas :: Ord a => [a] -> Tree a +gas = cata $ \case + Nil -> lef + Cons h t -> put h t + +dep :: Integral b => Tree a -> b +dep = getSum . cata alg where + alg = \case + LeafF -> mempty + NodeF l _ r -> Sum 1 <> max l r + +wyt :: Integral b => Tree a -> b +wyt = getSum . cata alg where + alg = \case + LeafF -> mempty + NodeF l _ r -> Sum 1 <> l <> r + +has :: Ord a => a -> Tree a -> Bool +has x = cata $ \case LeafF -> False NodeF l e r -> case compare x e of EQ -> True LT -> l GT -> r -fromList :: Ord a => [a] -> Tree a -fromList = foldr insert leaf +ver :: CPS.Tree a -> Tree a +ver = ana lag where + lag (project -> CPS.TreeF c) = c LeafF NodeF + +rev :: Tree a -> CPS.Tree a +rev = ana lag where + lag pin = case project pin of + LeafF -> CPS.lefF + NodeF l e r -> CPS.nodF e l r test0 :: Num a => Tree a -test0 = node 1 leaf leaf +test0 = nod 1 lef lef -- 1 -- | \ -- L L test1 :: Num a => Tree a -test1 = node 2 (node 1 leaf leaf) (node 3 leaf leaf) +test1 = nod 2 (nod 1 lef lef) (nod 3 lef lef) -- 2 -- | \ @@ -72,7 +181,7 @@ test1 = node 2 (node 1 leaf leaf) (node 3 leaf leaf) -- L L L L test2 :: (Ord a, Num a) => Tree a -test2 = insert 0 test1 +test2 = put 0 test1 -- 2 -- | \ @@ -83,7 +192,7 @@ test2 = insert 0 test1 -- L L test3 :: (Ord a, Num a) => Tree a -test3 = insert 5 test1 +test3 = put 5 test1 -- 2 -- | \ @@ -94,7 +203,7 @@ test3 = insert 5 test1 -- L L test4 :: (Ord a, Num a) => Tree a -test4 = insert 4 test3 +test4 = put 4 test3 -- 2 -- | \ diff --git a/lib/Okasaki/Tree/CPS.hs b/lib/Okasaki/Tree/CPS.hs @@ -0,0 +1,195 @@ +{-# OPTIONS_GHC -Wall -fno-warn-unused-top-binds #-} +{-# LANGUAGE DeriveFunctor #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE TypeFamilies #-} +{-# LANGUAGE ViewPatterns #-} + +module Okasaki.Tree.CPS ( + TreeF(..) + , Tree + , lef + , lefF + , nod + , nodF + + , put + , pet + , has + , haz + , gas + , wyt + , dep + + , test0 + , test1 + , test2 + , test3 + , test4 + ) where + +import Data.Fix hiding (cata, ana, hylo) +import Prelude hiding (sin) +import Data.Functor.Foldable +import Data.Monoid +import Okasaki.Orphans () + +newtype TreeF a r = TreeF (forall e. e -> (r -> a -> r -> e) -> e) + deriving Functor + +type Tree a = Fix (TreeF a) + +lefF :: TreeF a r +lefF = TreeF const + +nodF :: a -> r -> r -> TreeF a r +nodF x l r = TreeF (\_ c -> c l x r) + +lef :: Tree a +lef = Fix lefF + +nod :: a -> Tree a -> Tree a -> Tree a +nod x l r = Fix (nodF x l r) + +sin :: a -> Tree a +sin x = nod x lef lef + +empty :: Tree a -> Bool +empty (project -> TreeF c) = c True b where + b _ _ _ = False + +-- exercise 2.3 (no unnecessary copying) (?) +put :: Ord a => a -> Tree a -> Tree a +put x = apo lag where + lag (project -> TreeF c) = c a b + + a = nodF x (Left lef) (Left lef) + + b l e r = case compare x e of + EQ -> nodF e (Left l) (Left r) + LT -> nodF e (Right l) (Left r) + GT -> nodF e (Left l) (Right r) + +has :: Ord a => a -> Tree a -> Bool +has x = cata alg where + alg (TreeF c) = c False b + + b l e r = case compare x e of + EQ -> True + LT -> l + GT -> r + +-- exercise 2.2 (max d + 1 comparisons) +haz :: Ord a => a -> Tree a -> Bool +haz x t = case getLast (cata alg t) of + Nothing -> False + Just s -> s == x + where + alg (TreeF c) = c mempty b + + b l e r + | x < e = l + | otherwise = Last (Just e) <> r + +-- exercise 2.4 (no unnecessary copying, max d + 1 comparisons) +pet :: Ord a => a -> Tree a -> Tree a +pet x t = apo lag (Nothing, t) where + lag (s, project -> TreeF c) = c (a s) (b s) + + a = \case + Nothing -> nodF x (Left lef) (Left lef) + Just s + | s == x -> lefF + | otherwise -> nodF x (Left lef) (Left lef) + + b s l e r + | x < e = nodF e (Right (s, l)) (Left r) + | otherwise = nodF e (Left l) (Right (Just e, r)) + +-- exercise 2.5a (construct balanced binary trees of depth n) +dap :: Ord a => a -> Int -> Tree a +dap x n = ana lag (n, lef) where + lag (j, t) + | j <= 0 = lefF + | otherwise = + let s = (pred j, t) + in nodF x s s +-- exercise 2.5b (construct mostly-balanced binary trees of size n) +sap :: Ord a => a -> Int -> Tree a +sap x n = ana lag (n, lef) where + lag (j, t) + | j <= 0 = lefF + | odd j = + let s = (j `quot` 2, t) + in nodF x s s + | otherwise = + let l = j `quot` 2 + r = pred (j `quot` 2) + in nodF x (l, t) (r, t) + +gas :: Ord a => [a] -> Tree a +gas = cata $ \case + Nil -> lef + Cons h t -> put h t + +dep :: Integral b => Tree a -> b +dep = getSum . cata alg where + alg (TreeF c) = c mempty b + b l _ r = Sum 1 <> max l r + +wyt :: Integral b => Tree a -> b +wyt = getSum . cata alg where + alg (TreeF c) = c mempty b + b l _ r = Sum 1 <> l <> r + +test0 :: Num a => Tree a +test0 = nod 1 lef lef + +-- 1 +-- | \ +-- L L + +test1 :: Num a => Tree a +test1 = nod 2 (nod 1 lef lef) (nod 3 lef lef) + +-- 2 +-- | \ +-- 1 3 +-- | \ | \ +-- L L L L + +test2 :: (Ord a, Num a) => Tree a +test2 = pet 0 test1 + +-- 2 +-- | \ +-- 1 3 +-- | \ | \ +-- 0 L L L +-- | \ +-- L L + +test3 :: (Ord a, Num a) => Tree a +test3 = pet 5 test1 + +-- 2 +-- | \ +-- 1 3 +-- | \ | \ +-- L L L 5 +-- | \ +-- L L + +test4 :: (Ord a, Num a) => Tree a +test4 = pet 4 test3 + +-- 2 +-- | \ +-- 1 3 +-- | \ | \ +-- L L L 5 +-- | \ +-- 4 L +-- | \ +-- L L + diff --git a/okasaki.cabal b/okasaki.cabal @@ -12,12 +12,16 @@ cabal-version: >= 1.10 library default-language: Haskell2010 hs-source-dirs: lib + other-modules: + Okasaki.Orphans exposed-modules: Okasaki.Stack , Okasaki.Tree + , Okasaki.Tree.CPS build-depends: base , data-fix , deriving-compat , recursion-schemes + , free