data-list-zigzag-0.1.1.0: src/Data/List/ZigZag.hs
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveTraversable #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeFamilies #-}
-- | The feature of this module is 'ZigZag' and its class instances. It
-- is an abstract data type and can be constructed \/ deconstructed
-- by 'fromList' \/ 'toList' or 'fromDiagonals' \/ 'toDiagonals'. See the
-- associated documentation for more information.
--
module Data.List.ZigZag
( diagonals
, fromDiagonals
, fromList
, toDiagonals
, toList
, ZigZag()
)
where
--
import Control.Applicative
( Alternative(empty, (<|>))
, Applicative(pure, (<*>))
)
--
import Control.Monad
( ap
, join
, Monad(return, (>>=))
, MonadPlus(mzero, mplus)
)
--
import Data.Data
( Data
)
--
import Data.Foldable
( concat
, Foldable
)
--
import Data.Functor.Classes
( Eq1(liftEq)
, Ord1(liftCompare)
, Read1(liftReadList, liftReadsPrec)
, readsData
, readsUnaryWith
, showsUnaryWith
, Show1(liftShowList, liftShowsPrec)
)
--
import Data.List
( transpose
, unzip
, (++)
)
--
import Data.Maybe
( catMaybes
, Maybe(Just, Nothing)
)
--
import Data.Monoid
( Monoid(mappend, mempty)
)
--
import Data.Semigroup
( Semigroup((<>))
)
--
import Data.Traversable
( Traversable
)
--
import Data.Typeable
( Typeable
)
--
import GHC.Base
( Functor(fmap)
, (.)
, ($)
)
--
import GHC.Exts
( IsList(Item)
)
--
import qualified GHC.Exts as IsList
( fromList
, toList
)
--
import GHC.Generics
( Generic
, Generic1
)
--
import GHC.Read
( lexP
, parens
, Read(readPrec)
)
--
import GHC.Show
( Show(showsPrec)
, showParen
, showString
)
--
import Prelude
( Eq
, Ord((>))
)
--
import Text.ParserCombinators.ReadPrec
( prec
)
--
import Text.Read.Lex
( Lexeme(Ident)
)
--
newtype Diagonal a =
Diagonal
{ unDiagonal :: [a]
}
deriving
( Alternative
, Applicative
, Data
, Eq
, Eq1
, Foldable
, Functor
, Generic
, Generic1
, Monad
, MonadPlus
, Monoid
, Ord
, Ord1
, Read
, Semigroup
, Show
, Show1
, Traversable
, Typeable
)
--
instance IsList (Diagonal a) where
type (Item (Diagonal a)) = a
fromList = Diagonal
toList = unDiagonal
--
-- NOTE: also defined in the "these" package but it has too many
-- irrelevant dependencies.
data These a b =
This a
| That b
| Both a b
--
-- | A list but with a balanced enumeration of Cartesian product such
-- that
--
-- @
-- fmap sum (sequence (replicate n (fromList [0..])))
-- @
--
-- is monotonically increasing.
--
-- Example:
--
-- @
-- sequence [fromList [0,1], fromList [0,1,2]]
-- = fromDiagonals
-- [ [[0,0]]
-- , [[1,0],[0,1]]
-- , [[1,1],[0,2]]
-- , [[1,2]]
-- ]
-- @
--
-- This variation is useful in at least two ways. One, it is not stuck
-- on infinite factors. Two, if the factors are ordered then the
-- product is similarly ordered; this can lend to efficient searching
-- of product elements.
--
-- Note that this method fails for the infinitary product even if every
-- factor is known to be non-empty. The first element is known but
-- following it are infinite elements that each draw a second
-- element from one of the infinite factors. A product element drawing
-- a third factor element is never reached.
--
newtype ZigZag a =
ZigZag
{ unZigZag :: [Diagonal a]
}
deriving
( Data
, Eq
-- , Eq1
, Foldable
, Functor
, Generic
, Generic1
, Ord
-- , Ord1
, Traversable
, Typeable
)
--
instance Alternative ZigZag where
empty = ZigZag empty
(<|>) (ZigZag xs) (ZigZag ys) = ZigZag (tie f xs ys)
where
f (Both x y) = x <|> y
f (This x) = x
f (That y) = y
--
instance Applicative ZigZag where
pure = return
(<*>) = ap
--
instance Eq1 ZigZag where
liftEq eq (ZigZag xs) (ZigZag ys) = liftEq (liftEq eq) xs ys
--
instance IsList (ZigZag a) where
type (Item (ZigZag a)) = a
fromList = fromList
toList = toList
--
instance Monad ZigZag where
return = ZigZag . return . return
(>>=) (ZigZag xs) f =
ZigZag (fmap (join . Diagonal) (diagonals (fmap inner xs)))
where
inner =
fmap (Diagonal . concat)
. transpose
. unDiagonal
. fmap (fmap unDiagonal . unZigZag . f)
--
instance MonadPlus ZigZag where
mzero = empty
mplus = (<|>)
--
instance Monoid (ZigZag a) where
mempty = empty
mappend = (<|>)
--
instance Ord1 ZigZag where
liftCompare cmp (ZigZag xs) (ZigZag ys) =
liftCompare (liftCompare cmp) xs ys
--
--
instance Read a => Read (ZigZag a) where
readPrec = parens . prec 10 $ do
Ident "fromDiagonals" <- lexP
xs <- readPrec
return (fromDiagonals xs)
--
instance Read1 ZigZag where
liftReadsPrec rp rl =
readsData $
readsUnaryWith
(liftReadsPrec (liftReadsPrec rp rl) (liftReadList rp rl))
"fromDiagonals"
fromDiagonals
--
instance Semigroup (ZigZag a) where
(<>) = mappend
--
instance Show a => Show (ZigZag a) where
showsPrec p xs =
showParen (p > 10)
( showString "fromDiagonals "
. showsPrec 10 (toDiagonals xs)
)
--
instance Show1 ZigZag where
liftShowsPrec sp sl d m =
showsUnaryWith
(liftShowsPrec (liftShowsPrec sp sl) (liftShowList sp sl))
"fromDiagonals"
d
(toDiagonals m)
--
-- liftShowsPrec :: (Int -> [a] -> ShowS) -> ([[a]] -> ShowS) -> Int
-- -> [[a]] -> ShowS
-- | Finds the diagonals through a ragged list of lists.
--
-- For example, the diagonals of:
--
-- @
-- [ [0,1,2]
-- , []
-- , [3,4]
-- , [5,6,7]
-- ]
-- @
--
-- Are:
--
-- @
-- [ [0]
-- , [1]
-- , [3,2]
-- , [5,4]
-- , [6]
-- , [7]
-- ]
-- @
--
-- Which can be seen intuitively.
--
-- This algorithm works by storing a list of tails of rows already
-- seen. To find the next diagonal we take the head of the next row
-- plus the head of each stored tail. The tail remainders are stored
-- plus the remainder of the new row.
--
-- If there are no more rows but some remaining tails we then
-- iteratively form diagonals from the heads of each tail until there
-- are no tails remaining.
--
-- Applied to the example:
--
-- @
-- Row | Output | Remaining
-- --------+--------+----------------
-- [0,1,2] | [0] | [[1,2]]
-- [] | [1] | [[2]]
-- [3,4] | [3,2] | [[4]]
-- [5,6,7] | [5,4] | [[6,7]]
-- x | [6] | [[7]]
-- x | [7] | []
-- @
--
diagonals :: [[a]] -> [[a]]
diagonals = h []
where
h rem xxs =
let (heads, tails) = peel rem
in case xxs of
((y:ys):xs) -> (y : heads) : h (ys : tails) xs
([]:xs) -> heads : h tails xs
[] ->
case heads of
(_:_) -> heads : transpose tails
[] -> transpose tails
peel = unzip . catMaybes . fmap uncons
--
-- | Convert a list of diagonals to a ZigZag.
--
-- @
-- fromDiagonals . toDiagonals = id
-- toDiagonals . fromDiagonals = id
-- @
--
fromDiagonals :: [[a]] -> ZigZag a
fromDiagonals = ZigZag . fmap Diagonal
-- | Convert a list to a ZigZag.
--
-- @
-- fromList . toList = id
-- toList . fromList = id
-- @
--
fromList :: [a] -> ZigZag a
fromList = ZigZag . fmap (Diagonal . return)
-- | Zips up to the longest list rather than 'GHC.List.zip' which zips
-- up to the shortest list.
--
-- Example:
--
-- @
-- tie id [1] [3,4] = [Both 1 3, That 4]
-- @
--
tie :: (These a b -> c) -> [a] -> [b] -> [c]
tie f (x:xs) (y:ys) = f (Both x y) : tie f xs ys
tie f (x:xs) [] = f (This x) : tie f xs []
tie f [] (y:ys) = f (That y) : tie f [] ys
tie f [] [] = []
-- | Convert a ZigZag to a list of diagonals.
--
-- @
-- fromDiagonals . toDiagonals = id
-- toDiagonals . fromDiagonals = id
-- @
--
toDiagonals :: ZigZag a -> [[a]]
toDiagonals = fmap unDiagonal . unZigZag
-- | Convert a ZigZag to a list.
--
-- @
-- fromList . toList = id
-- toList . fromList = id
-- @
--
toList :: ZigZag a -> [a]
toList = concat . fmap unDiagonal . unZigZag
-- | Undo '(:)'.
--
uncons :: [a] -> Maybe (a, [a])
uncons (x:xs) = Just (x, xs)
uncons [] = Nothing