Safe Haskell | Safe |
---|---|
Language | Haskell98 |
Simple but efficient lazy sequence type based on a nested data type and polymorphic recursion.
It is like a list, but instead of O(1)
cons/uncons and O(k)
lookup,
we have amortized O(1)
cons/uncons and O(log(k))
lookup (both are O(log(n))
in the worst case).
This is somewhat similar to a finger tree (which is also represented by a nested data type), but
much simpler and more memory efficient (memory usage is basically the same as with lists: amortized
3 words per element, plus the data itself).
However, modifying the right end of the sequence is still slow: O(n)
. This affects the functions
snoc
, unSnoc
, append
, take
, init
. Somewhat surprisingly, extracting the last element is
still fast.
An example usage is a pure random-access stack.
This module is intended to be imported qualified.
- data Seq a
- cons :: a -> Seq a -> Seq a
- unCons :: Seq a -> Maybe (a, Seq a)
- null :: Seq a -> Bool
- length :: Seq a -> Int
- empty :: Seq a
- toList :: Seq a -> [a]
- fromList :: [a] -> Seq a
- singleton :: a -> Seq a
- pair :: a -> a -> Seq a
- triple :: a -> a -> a -> Seq a
- quad :: a -> a -> a -> a -> Seq a
- head :: Seq a -> a
- tail :: Seq a -> Seq a
- last :: Seq a -> a
- mbHead :: Seq a -> Maybe a
- mbTail :: Seq a -> Maybe (Seq a)
- tails :: Seq a -> [Seq a]
- mbLast :: Seq a -> Maybe a
- lookup :: Int -> Seq a -> a
- mbLookup :: Int -> Seq a -> Maybe a
- update :: (a -> a) -> Int -> Seq a -> Seq a
- replace :: Int -> a -> Seq a -> Seq a
- drop :: Int -> Seq a -> Seq a
- append :: Seq a -> Seq a -> Seq a
- take :: Int -> Seq a -> Seq a
- init :: Seq a -> Seq a
- mbInit :: Seq a -> Maybe (Seq a)
- snoc :: Seq a -> a -> Seq a
- unSnoc :: Seq a -> Maybe (Seq a, a)
- toListNaive :: Seq a -> [a]
- checkInvariant :: Seq a -> Bool
- showInternal :: Show a => Seq a -> String
- graphviz :: Show a => Seq a -> String
Documentation
The lazy sequence type.
The underlying (nested) data structure corresponds to the binary representation of the length of the list. It looks like this:
data Seq a = Nil | Even (Seq (a,a)) | Odd a (Seq (a,a))
Furthermore we maintain the invariant that Even Nil
never appears.
For example, here are how sequences of lengths 4, 5, 6 and 7 are represented:
Accessing the left end of the sequence
Basic queries
Basic construction
Short sequences
Unsafe head and tail
Safe head and tail
mbHead :: Seq a -> Maybe a Source
First element of the sequence. Worst case O(log(n))
, amortized O(1)
.
Indexing
lookup :: Int -> Seq a -> a Source
Lookup the k
-th element of a sequence. This is worst case O(log(n))
and amortized O(log(k))
, and quite efficient.
replace :: Int -> a -> Seq a -> Seq a Source
Replace the k
-th element. replace n x == update (const x) n
drop :: Int -> Seq a -> Seq a Source
Drop is efficient: drop k
is amortized O(log(k))
, worst case maybe O(log(n)^2)
?
Slow operations
append :: Seq a -> Seq a -> Seq a Source
O(n)
(for large n
at least), where n
is the length of the first sequence.
mbInit :: Seq a -> Maybe (Seq a) Source
The sequence without the last element. Warning, this is slow, O(n)
unSnoc :: Seq a -> Maybe (Seq a, a) Source
Stripping the last element from a sequence is a slow operation, O(n)
.
If you only need extracting the last element, use mbLast
instead,
which is fast.
Debugging
toListNaive :: Seq a -> [a] Source
Naive implementation of toList
checkInvariant :: Seq a -> Bool Source
We maintain the invariant that (Z Nil)
never appears. This function
checks whether this is satisfied. Used only for testing.
showInternal :: Show a => Seq a -> String Source
Show the internal structure of the sequence. The constructor names
Z
and O
come from "zero" and "one", respectively.