unfoldable 0.9 → 0.9.1
raw patch · 7 files changed
+126/−76 lines, 7 files
Files
- CHANGELOG.md +5/−0
- examples/brackets.hs +30/−0
- examples/btree.hs +0/−1
- examples/redblack.hs +26/−27
- src/Data/Unfoldable.hs +35/−29
- src/Data/Unfolder.hs +29/−18
- unfoldable.cabal +1/−1
CHANGELOG.md view
@@ -1,3 +1,8 @@+0.9.1+-----+* added `Nth` unfolder+* added brackets example+ 0.9 --- * added `chooseMap` method
+ examples/brackets.hs view
@@ -0,0 +1,30 @@+-- From https://byorgey.wordpress.com/2016/10/25/adventures-in-enumerating-balanced-brackets/+import Data.Unfolder+import Data.MemoTrie (memo2)+import Control.Applicative++enumBrackets :: Unfolder f => Int -> f String+enumBrackets n = enumBracketsTail n 0++enumBracketsTail :: Unfolder f => Int -> Int -> f String+enumBracketsTail = enumBracketsTail'+ where+ -- Ensure memoization happens for a specific `f`+ enumBracketsTail' = memo2 enumBracketsTail''+ enumBracketsTail'' 0 c = pure (replicate c ')')+ enumBracketsTail'' n 0 = ('(':) <$> enumBracketsTail' (n-1) 1+ enumBracketsTail'' n c =+ ('(':) <$> enumBracketsTail' (n-1) (c+1)+ <|>+ ((')':) <$> enumBracketsTail' n (c-1))++{-++>>> enumBrackets 3 :: [String]+["((()))","(()())","(())()","()(())","()()()"]+>>> getNth (enumBrackets 40) 16221270422764920820+"((((((((()((())()(()()()())(()))((()()()()(()((()())))((()())))))))()))()())()))"+>>> size (enumBrackets 100)+896519947090131496687170070074100632420837521538745909320++-}
examples/btree.hs view
@@ -1,4 +1,3 @@-import Control.Applicative import Data.Unfoldable import Data.Unfolder
examples/redblack.hs view
@@ -7,7 +7,6 @@ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UndecidableInstances #-} -import Control.Applicative import Data.Unfoldable import Data.Unfolder @@ -15,54 +14,54 @@ import System.Random import Data.List (intercalate) --- red-black tree implementation adapted from https://gist.github.com/2660297-data Nat = Zero | Succ Nat deriving (Eq, Ord, Show)+-- Red-Black tree implementation adapted from https://gist.github.com/2660297+data Nat = Zero | Succ Nat data NatW :: Nat -> * where- ZeroW :: NatW Zero- SuccW :: NatW n -> NatW (Succ n)+ ZeroW :: NatW 'Zero+ SuccW :: NatW n -> NatW ('Succ n) -data RedBlack = Black | Red deriving (Eq, Ord, Show)+data RedBlack = Black | Red -data RedBlackTree a where - T :: Node Black n a -> RedBlackTree a+data RedBlackTree a where+ T :: Node 'Black n a -> RedBlackTree a deriving instance Show a => Show (RedBlackTree a) data Node :: RedBlack -> Nat -> * -> * where- Leaf :: Node Black Zero a- B :: Node cL n a -> a -> Node cR n a -> Node Black (Succ n) a- R :: Node Black n a -> a -> Node Black n a -> Node Red n a+ Leaf :: Node 'Black 'Zero a+ B :: Node cL n a -> a -> Node cR n a -> Node 'Black ('Succ n) a+ R :: Node 'Black n a -> a -> Node 'Black n a -> Node 'Red n a deriving instance Show a => Show (Node c n a) instance Unfoldable RedBlackTree where unfold = u ZeroW where- u :: forall n f a. (Unfoldable (Node Black n), Unfolder f) + u :: forall n f a. (Unfoldable (Node 'Black n), Unfolder f) => NatW n -> f a -> f (RedBlackTree a)- u n fa = choose - [ T <$> (unfold :: Unfolder f => f a -> f (Node Black n a)) fa+ u n fa = choose+ [ T <$> (unfold :: f a -> f (Node 'Black n a)) fa , u (SuccW n) fa ]- -instance Unfoldable (Node Black Zero) where++instance Unfoldable (Node 'Black 'Zero) where unfold _ = choose [ pure Leaf ] -instance Unfoldable (Node Black n) => Unfoldable (Node Black (Succ n)) where+instance Unfoldable (Node 'Black n) => Unfoldable (Node 'Black ('Succ n)) where unfold = u where- u :: forall f a. Unfolder f => f a -> f (Node Black (Succ n) a)- u fa = choose + u :: forall f a. Unfolder f => f a -> f (Node 'Black ('Succ n) a)+ u fa = choose [ B <$> b <*> fa <*> b , B <$> b <*> fa <*> r , B <$> r <*> fa <*> b , B <$> r <*> fa <*> r ] where- r :: Unfolder f => f (Node Red n a)+ r :: f (Node 'Red n a) r = unfold fa- b :: Unfolder f => f (Node Black n a)+ b :: f (Node 'Black n a) b = unfold fa -instance Unfoldable (Node Black n) => Unfoldable (Node Red n) where+instance Unfoldable (Node 'Black n) => Unfoldable (Node 'Red n) where unfold fa = choose [ R <$> unfold fa <*> fa <*> unfold fa ] rbtree :: Int -> RedBlackTree Int@@ -76,9 +75,9 @@ newtype Formula = Formula [Integer] instance Show Formula where- show (Formula xs) + show (Formula xs) | all (== 0) xs = "0"- | otherwise = intercalate " + " $ map showOne $ filter ((/=0) . fst) $ zip xs [0..]+ | otherwise = intercalate " + " $ map showOne $ filter ((/=0) . fst) $ zip xs [(0::Int)..] where showOne (x, 0) = show x showOne (1, 1) = "x"@@ -99,9 +98,9 @@ Formula xs + Formula ys = Formula (add xs ys) Formula xs * Formula ys = Formula (mul xs ys) -x :: Formula-x = Formula [0, 1]+varX :: Formula+varX = Formula [0, 1] -- See http://oeis.org/A001137 rbFormula :: Int -> NumConst Formula (RedBlackTree a)-rbFormula d = ala (limitDepth d) unfold (NumConst x)+rbFormula d = ala (limitDepth d) unfold (NumConst varX)
src/Data/Unfoldable.hs view
@@ -10,19 +10,19 @@ -- -- Class of data structures that can be unfolded. ------------------------------------------------------------------------------{-# LANGUAGE CPP, Safe #-}+{-# LANGUAGE CPP, Safe, TupleSections #-} #ifdef GENERICS {-# LANGUAGE TypeOperators, DefaultSignatures, FlexibleContexts #-} #endif-module Data.Unfoldable +module Data.Unfoldable (- + -- * Unfoldable Unfoldable(..) , unfold_ , unfoldBF , unfoldBF_- + -- ** Specific unfolds , unfoldr , fromList@@ -33,10 +33,10 @@ , allBreadthFirst , randomDefault , arbitraryDefault- - ) ++ ) where- + import Control.Applicative import Data.Unfolder import Data.Functor.Compose@@ -50,6 +50,7 @@ import Test.QuickCheck (Arbitrary(..), Gen, sized, resize) import Data.Maybe import qualified Data.Sequence as S+import qualified Data.Tree as T #ifdef GENERICS import GHC.Generics@@ -83,23 +84,23 @@ -- > import GHC.Generics -- > -- > data Tree a = Empty | Leaf a | Node (Tree a) a (Tree a) deriving Generic1--- > +-- > -- > instance Unfoldable Tree class Unfoldable t where -- | Given a way to generate elements, return a way to generate structures containing those elements. unfold :: Unfolder f => f a -> f (t a)- + #ifdef GENERICS- + default unfold :: (Generic1 t, GUnfold (Rep1 t), Unfolder f) => f a -> f (t a) unfold fa = to1 <$> choose (gunfold fa)- + class GUnfold r where gunfold :: Unfolder f => f a -> [f (r a)]- + instance GUnfold V1 where gunfold _ = []- + instance GUnfold U1 where gunfold _ = [pure U1] @@ -114,7 +115,7 @@ instance GUnfold f => GUnfold (M1 i c f) where gunfold fa = map (M1 <$>) (gunfold fa)- + instance (GUnfold f, GUnfold g) => GUnfold (f :+: g) where gunfold fa = map (L1 <$>) (gunfold fa) ++ map (R1 <$>) (gunfold fa) @@ -139,7 +140,7 @@ unfoldBF_ = bfs unfold_ -- | @unfoldr@ builds a data structure from a seed value. It can be specified as:--- +-- -- > unfoldr f z == fromList (Data.List.unfoldr f z) unfoldr :: Unfoldable t => (b -> Maybe (a, b)) -> b -> Maybe (t a) unfoldr f z = terminate . flip runStateT z . unfoldBF . StateT $ maybeToList . f@@ -182,39 +183,39 @@ -- | Provides a QuickCheck generator, can be used as default instance for 'Arbitrary'. arbitraryDefault :: (Arbitrary a, Unfoldable t) => Gen (t a)-arbitraryDefault = let Arb _ gen = unfold arbUnit in +arbitraryDefault = let Arb _ gen = unfold arbUnit in fromMaybe (error "Failed to generate a value.") <$> sized (\n -> resize (n + 1) gen) instance Unfoldable [] where- unfold fa = choose - [ pure []- , (:) <$> fa <*> unfold fa- ]+ unfold fa = go where+ go = choose+ [ pure []+ , (:) <$> fa <*> go ] instance Unfoldable Maybe where- unfold fa = choose + unfold fa = choose [ pure Nothing , Just <$> fa ] instance (Bounded a, Enum a) => Unfoldable (Either a) where- unfold fa = choose + unfold fa = choose [ Left <$> boundedEnum , Right <$> fa ] instance (Bounded a, Enum a) => Unfoldable ((,) a) where- unfold fa = choose + unfold fa = choose [ (,) <$> boundedEnum <*> fa ] instance Unfoldable Identity where- unfold fa = choose + unfold fa = choose [ Identity <$> fa ] instance (Bounded a, Enum a) => Unfoldable (Constant a) where- unfold _ = choose + unfold _ = choose [ Constant <$> boundedEnum ]- + instance (Unfoldable p, Unfoldable q) => Unfoldable (Product p q) where unfold fa = choose [ Pair <$> unfold fa <*> unfold fa ]@@ -234,6 +235,11 @@ [ Reverse <$> getDualA (unfold (DualA fa)) ] instance Unfoldable S.Seq where- unfold fa = choose- [ pure empty- , (S.<|) <$> fa <*> unfold fa ]+ unfold fa = go where+ go = choose+ [ pure empty+ , (S.<|) <$> fa <*> go ]++instance Unfoldable T.Tree where+ unfold fa = go where+ go = choose [ T.Node <$> fa <*> unfold go ]
src/Data/Unfolder.hs view
@@ -44,6 +44,7 @@ , arbUnit , NumConst(..)+ , Nth(..) -- * UnfolderTransformer , UnfolderTransformer(..)@@ -75,7 +76,6 @@ import Data.Functor.Reverse import Control.Applicative.Backwards import Control.Applicative.Lift-import Control.Monad.Trans.Error import Control.Monad.Trans.Except import Control.Monad.Trans.List import Control.Monad.Trans.Maybe@@ -88,23 +88,21 @@ import Test.QuickCheck (Arbitrary(..), Gen, oneof, elements, sized, resize) import Data.Monoid (Monoid(..))-import Data.Maybe (catMaybes, listToMaybe)-import Data.Foldable (asum, foldl')-import Data.Traversable (traverse)+import Data.Maybe (catMaybes) import qualified Data.Sequence as S -- | Unfolders provide a way to unfold data structures. -- The methods have default implementations in terms of 'Alternative', -- but you can implement 'chooseMap' to act on recursive positions of the -- data structure, or simply to provide a faster implementation than--- 'asum . map f'.+-- 'foldr ((<|>) . f) empty'. class Alternative f => Unfolder f where -- | Choose one of the values from the list. choose :: [f a] -> f a choose = chooseMap id -- | Choose one of the values from the list and apply the given function. chooseMap :: (a -> f b) -> [a] -> f b- chooseMap f = asum . map f+ chooseMap f = foldr ((<|>) . f) empty -- | Given a number 'n', return a number between '0' and 'n - 1'. chooseInt :: Int -> f Int chooseInt n = chooseMap pure [0 .. n - 1]@@ -132,7 +130,7 @@ -- of the higher values. This means that f.e. breadth-first unfolding and arbitrary will prefer -- lower values. betweenD :: (Unfolder f, Enum a) => a -> a -> f a-betweenD lb ub = betweenD' lb (fromEnum ub - fromEnum lb)+betweenD lb0 ub = betweenD' lb0 (fromEnum ub - fromEnum lb0) where betweenD' lb n | n < 0 = empty | otherwise = choose [pure lb, betweenD' (succ lb) (pred n)]@@ -189,9 +187,6 @@ instance Unfolder f => Unfolder (Lift f) -- | Derived instance.-instance (Functor m, Monad m, Error e) => Unfolder (ErrorT e m)---- | Derived instance. instance (Functor m, Monad m, Monoid e) => Unfolder (ExceptT e m) -- | Derived instance.@@ -229,6 +224,11 @@ instance (Monoid w, Unfolder m) => Unfolder (WriterT w m) where chooseMap f = WriterT . chooseMap (runWriterT . f) +-- | Don't choose but return all items.+instance Unfolder S.Seq where+#if MIN_VERSION_containers(0,5,6)+ chooseInt n = S.fromFunction n id+#endif newtype Random g m a = Random { getRandom :: StateT g m a }@@ -247,6 +247,24 @@ chooseInt n = Random . StateT $ return . R.randomR (0, n - 1) +data Nth a = Nth+ { size :: Integer+ , getNth :: Integer -> a+ }+instance Functor Nth where+ fmap f (Nth sizeA as) = Nth sizeA (f . as)+instance Applicative Nth where+ pure a = Nth 1 (const a)+ Nth sizeF fs <*> Nth sizeA as = Nth (sizeF * sizeA) $ \n ->+ let (l, r) = n `divMod` sizeA in fs l (as r)+instance Alternative Nth where+ empty = Nth 0 (const undefined)+ Nth sizeA as <|> Nth sizeB bs = Nth (sizeA + sizeB) $ \n ->+ if n < sizeA then as n else bs (n - sizeA)+instance Unfolder Nth where+ chooseInt n = Nth (toInteger n) fromInteger++ -- | An 'UnfolderTransformer' changes the way an 'Unfolder' unfolds. class UnfolderTransformer t where -- | Lift a computation from the argument unfolder to the constructed unfolder.@@ -404,11 +422,4 @@ empty = NumConst 0 NumConst a <|> NumConst b = NumConst $ a + b -- | Unfolds to a constant numeric value. Useful for counting shapes.-instance Num a => Unfolder (NumConst a) where- chooseMap f = foldr ((<|>) . f) empty--instance Unfolder S.Seq where- chooseMap f = foldr ((<|>) . f) empty-#if MIN_VERSION_containers(0,5,6)- chooseInt n = S.fromFunction n id-#endif+instance Num a => Unfolder (NumConst a)
unfoldable.cabal view
@@ -1,5 +1,5 @@ Name: unfoldable-Version: 0.9+Version: 0.9.1 Synopsis: Class of data structures that can be unfolded. Description: Just as there's a Foldable class, there should also be an Unfoldable class. .