| Safe Haskell | Safe |
|---|---|
| Language | Haskell2010 |
Data.Ranges
Description
The primary interface to the range library.
A Range describes a membership set over any Ord type. This module
provides the Ranges type — a canonicalised, indexed collection of
Range values — along with construction operators, set operations, and
membership predicates.
Quick start
Build ranges with the construction operators and combine them with (:<>)
>>>(1 +=+ 5 :: Ranges Integer) <> (3 +=+ 8)Ranges [1 +=+ 8]
Test membership:
>>>inRanges (1 +=+ 10 <> 20 +=+ 30 :: Ranges Integer) 5True>>>inRanges (1 +=+ 10 <> 20 +=+ 30 :: Ranges Integer) 15False
Use mconcat to build from a list:
>>>mconcat [1 +=+ 5, 10 +=+ 15, 12 +=+ 20 :: Ranges Integer]Ranges [1 +=+ 5,10 +=+ 20]
Transforming ranges
Ranges does not implement Functor. Mapping a function over boundary
values is not a well-defined operation for half-infinite ranges: an
order-reversing function like negate applied to lbi would need to
produce ubi, but Functor cannot express that structural flip.
The idiomatic alternative is to map the query value, not the ranges. Instead of converting boundaries to a new domain, convert incoming queries back to the range's domain:
-- Unit conversion: test a Fahrenheit value against Celsius ranges let safeTemp = 20 +=+ 37 :: Ranges Double -- defined in °C let inSafeTemp f = inRanges safeTemp ((f - 32) * 5 / 9)
This is always correct regardless of whether the conversion is monotone, never requires re-canonicalisation, and avoids the constructor-flip hazard.
Module guide
- Data.Ranges — start here.
Rangestype, all set operations. - Data.Range — deprecated re-export shim; use Data.Ranges instead.
- Data.Range.Ord —
KeyRangeandSortedRangeforOrd-requiring contexts. - Data.Range.Parser — Parsec-based parser for range strings.
- Data.Range.Algebra — F-Algebra for deferred, efficient expression trees.
Synopsis
- data Range a
- = SingletonRange a
- | SpanRange (Bound a) (Bound a)
- | LowerBoundRange (Bound a)
- | UpperBoundRange (Bound a)
- | InfiniteRange
- data Bound a = Bound {
- boundValue :: a
- boundType :: BoundType
- data BoundType
- data Ranges a
- (+=+) :: Ord a => a -> a -> Ranges a
- (+=*) :: Ord a => a -> a -> Ranges a
- (*=+) :: Ord a => a -> a -> Ranges a
- (*=*) :: Ord a => a -> a -> Ranges a
- lbi :: Ord a => a -> Ranges a
- lbe :: Ord a => a -> Ranges a
- ubi :: Ord a => a -> Ranges a
- ube :: Ord a => a -> Ranges a
- inf :: Ord a => Ranges a
- inRange :: Ord a => Range a -> a -> Bool
- aboveRange :: Ord a => Range a -> a -> Bool
- belowRange :: Ord a => Range a -> a -> Bool
- rangesOverlap :: Ord a => Range a -> Range a -> Bool
- rangesAdjoin :: Ord a => Range a -> Range a -> Bool
- inRanges :: Ord a => Ranges a -> a -> Bool
- aboveRanges :: Ord a => Ranges a -> a -> Bool
- belowRanges :: Ord a => Ranges a -> a -> Bool
- mergeRanges :: Ord a => [Range a] -> Ranges a
- union :: Ord a => Ranges a -> Ranges a -> Ranges a
- intersection :: Ord a => Ranges a -> Ranges a -> Ranges a
- difference :: Ord a => Ranges a -> Ranges a -> Ranges a
- invert :: Ord a => Ranges a -> Ranges a
- fromRanges :: (Ord a, Enum a) => Ranges a -> [a]
- joinRanges :: (Ord a, Enum a) => Ranges a -> Ranges a
Core types
The Range Data structure; it is capable of representing any type of range. This is the primary data structure in this library. Everything should be possible to convert back into this datatype. All ranges in this structure are inclusively bound.
Constructors
| SingletonRange a | Represents a single element as a range. |
| SpanRange (Bound a) (Bound a) | Represents a bounded span of elements. The first argument is expected to be less than or equal to the second argument. |
| LowerBoundRange (Bound a) | Represents a range with a finite lower bound and an infinite upper bound. |
| UpperBoundRange (Bound a) | Represents a range with an infinite lower bound and a finite upper bound. |
| InfiniteRange | Represents an infinite range over all values. |
Instances
| NFData a => NFData (Range a) Source # | |||||
Defined in Data.Range.Data | |||||
| Generic (Range a) Source # | |||||
Defined in Data.Range.Data Associated Types
| |||||
| Show a => Show (Range a) Source # | |||||
| Eq a => Eq (Range a) Source # | |||||
| Ord a => RangeAlgebra [Range a] Source # | Evaluates to a merged, canonical list of non-overlapping ranges.
Used internally by Data.Ranges and useful when you need to inspect
individual | ||||
| type Rep (Range a) Source # | |||||
Defined in Data.Range.Data type Rep (Range a) = D1 ('MetaData "Range" "Data.Range.Data" "range-1.0.0.0-G3FrXHtIb7F4RzvqODngXx" 'False) ((C1 ('MetaCons "SingletonRange" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 a)) :+: C1 ('MetaCons "SpanRange" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Bound a)) :*: S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Bound a)))) :+: (C1 ('MetaCons "LowerBoundRange" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Bound a))) :+: (C1 ('MetaCons "UpperBoundRange" 'PrefixI 'False) (S1 ('MetaSel ('Nothing :: Maybe Symbol) 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 (Bound a))) :+: C1 ('MetaCons "InfiniteRange" 'PrefixI 'False) (U1 :: Type -> Type)))) | |||||
Represents a bound at a particular value with a BoundType.
There is no implicit understanding if this is a lower or upper bound, it could be either.
Constructors
| Bound | |
Fields
| |
Instances
| Functor Bound Source # | |||||
| NFData a => NFData (Bound a) Source # | |||||
Defined in Data.Range.Data | |||||
| Generic (Bound a) Source # | |||||
Defined in Data.Range.Data Associated Types
| |||||
| Show a => Show (Bound a) Source # | |||||
| Eq a => Eq (Bound a) Source # | |||||
| Ord a => Ord (Bound a) Source # | |||||
| type Rep (Bound a) Source # | |||||
Defined in Data.Range.Data type Rep (Bound a) = D1 ('MetaData "Bound" "Data.Range.Data" "range-1.0.0.0-G3FrXHtIb7F4RzvqODngXx" 'False) (C1 ('MetaCons "Bound" 'PrefixI 'True) (S1 ('MetaSel ('Just "boundValue") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 a) :*: S1 ('MetaSel ('Just "boundType") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedLazy) (Rec0 BoundType))) | |||||
Represents a type of boundary.
Constructors
| Inclusive | The value at the boundary should be included in the bound. |
| Exclusive | The value at the boundary should be excluded in the bound. |
Instances
| NFData BoundType Source # | |||||
Defined in Data.Range.Data | |||||
| Generic BoundType Source # | |||||
Defined in Data.Range.Data Associated Types
| |||||
| Show BoundType Source # | |||||
| Eq BoundType Source # | |||||
| Ord BoundType Source # | |||||
Defined in Data.Range.Data | |||||
| type Rep BoundType Source # | |||||
Defined in Data.Range.Data type Rep BoundType = D1 ('MetaData "BoundType" "Data.Range.Data" "range-1.0.0.0-G3FrXHtIb7F4RzvqODngXx" 'False) (C1 ('MetaCons "Inclusive" 'PrefixI 'False) (U1 :: Type -> Type) :+: C1 ('MetaCons "Exclusive" 'PrefixI 'False) (U1 :: Type -> Type)) | |||||
The Ranges type
A set of ranges represented as a merged, canonical list of
non-overlapping Range values, with pre-built O(log n) membership,
O(1) above, and O(1) below predicates.
Construct values with the operators (+=+, lbi, etc.) or with
mergeRanges. Combine with ( or <>)mconcat.
Semigroup: ( computes the set union and merges the result into
canonical form.<>)
>>>(1 +=+ 5 :: Ranges Integer) <> (3 +=+ 8)Ranges [1 +=+ 8]
Monoid: mempty is the empty set. mconcat merges an entire list in a
single pass, more efficiently than repeated (:<>)
>>>mconcat [1 +=+ 5, 10 +=+ 15, 12 +=+ 20 :: Ranges Integer]Ranges [1 +=+ 5,10 +=+ 20]
Use unRanges to extract the underlying list.
Instances
| NFData a => NFData (Ranges a) Source # | Forces the canonical range list; the cached predicate closure is not forced (it is derived from the list and adds no new thunks). |
Defined in Data.Ranges | |
| Ord a => Monoid (Ranges a) Source # | |
| Ord a => Semigroup (Ranges a) Source # | |
| Show a => Show (Ranges a) Source # | |
| Eq a => Eq (Ranges a) Source # | Two |
| Ord a => RangeAlgebra (Ranges a) Source # | Evaluates a This is the primary evaluation target for user-facing algebra expressions.
The implementation converts leaves to |
Range creation
Each operator constructs a single-element Ranges. Because Ranges is a
Semigroup, you can combine them directly with <>:
>>>(1 +=+ 5 :: Ranges Integer) <> (3 +=+ 8)Ranges [1 +=+ 8]
The operators mirror those in Data.Range.Operators but return Ranges
instead of Range, so they compose naturally without wrapping.
(+=+) :: Ord a => a -> a -> Ranges a Source #
Mathematically equivalent to [x, y]. See SpanRange for the
underlying constructor.
>>>1 +=+ 5 :: Ranges IntegerRanges [1 +=+ 5]
(+=*) :: Ord a => a -> a -> Ranges a Source #
Mathematically equivalent to [x, y).
>>>1 +=* 5 :: Ranges IntegerRanges [1 +=* 5]
(*=+) :: Ord a => a -> a -> Ranges a Source #
Mathematically equivalent to (x, y].
>>>1 *=+ 5 :: Ranges IntegerRanges [1 *=+ 5]
(*=*) :: Ord a => a -> a -> Ranges a Source #
Mathematically equivalent to (x, y).
>>>1 *=* 5 :: Ranges IntegerRanges [1 *=* 5]
lbi :: Ord a => a -> Ranges a Source #
Mathematically equivalent to [x, ∞).
>>>lbi 5 :: Ranges IntegerRanges [lbi 5]
Single-range predicates
inRange :: Ord a => Range a -> a -> Bool Source #
Returns True if the value falls within the single range.
Respects Inclusive and Exclusive bounds.
See inRanges for testing against a Ranges collection.
>>>inRange (SpanRange (Bound 1 Inclusive) (Bound 10 Inclusive)) (5 :: Integer)True>>>inRange (SpanRange (Bound 1 Inclusive) (Bound 10 Exclusive)) (10 :: Integer)False
aboveRange :: Ord a => Range a -> a -> Bool Source #
Returns True if the value is strictly above (greater than the upper
bound of) the given range.
>>>aboveRange (SpanRange (Bound 1 Inclusive) (Bound 5 Inclusive)) (6 :: Integer)True>>>aboveRange (LowerBoundRange (Bound 0 Inclusive)) (6 :: Integer)False
belowRange :: Ord a => Range a -> a -> Bool Source #
Returns True if the value is strictly below (less than the lower
bound of) the given range.
>>>belowRange (SpanRange (Bound 1 Inclusive) (Bound 5 Inclusive)) (0 :: Integer)True>>>belowRange (UpperBoundRange (Bound 6 Inclusive)) (0 :: Integer)False
rangesOverlap :: Ord a => Range a -> Range a -> Bool Source #
Returns True if two ranges share at least one value.
>>>rangesOverlap (SpanRange (Bound 1 Inclusive) (Bound 5 Inclusive)) (SpanRange (Bound 3 Inclusive) (Bound 7 Inclusive) :: Range Integer)True>>>rangesOverlap (SpanRange (Bound 1 Inclusive) (Bound 5 Exclusive)) (SpanRange (Bound 5 Inclusive) (Bound 7 Inclusive) :: Range Integer)False
rangesAdjoin :: Ord a => Range a -> Range a -> Bool Source #
Returns True if two ranges touch at a single exclusive boundary but
share no values.
>>>rangesAdjoin (SpanRange (Bound 1 Inclusive) (Bound 5 Exclusive)) (SpanRange (Bound 5 Inclusive) (Bound 7 Inclusive) :: Range Integer)True>>>rangesAdjoin (SpanRange (Bound 1 Inclusive) (Bound 5 Inclusive)) (SpanRange (Bound 3 Inclusive) (Bound 7 Inclusive) :: Range Integer)False
Multi-range predicates
inRanges :: Ord a => Ranges a -> a -> Bool Source #
Returns True if the value falls within any of the given ranges.
The membership predicate is pre-built when the Ranges value is
constructed, so each call is O(log n) in the number of spans. Partial
application is idiomatic:
let memberOf = inRanges myRanges filter memberOf largeList
>>>inRanges (1 +=+ 10 <> 20 +=+ 30 :: Ranges Integer) 5True>>>inRanges (1 +=+ 10 <> 20 +=+ 30 :: Ranges Integer) 15False
aboveRanges :: Ord a => Ranges a -> a -> Bool Source #
Returns True if the value is strictly above all of the given ranges.
This predicate is O(1): the answer is determined by the last element of the canonical range list (which has the largest upper bound), cached at construction time.
>>>aboveRanges (1 +=+ 5 <> 10 +=+ 15 :: Ranges Integer) 20True>>>aboveRanges (1 +=+ 5 <> lbi 10 :: Ranges Integer) 20False
belowRanges :: Ord a => Ranges a -> a -> Bool Source #
Returns True if the value is strictly below all of the given ranges.
This predicate is O(1): the answer is determined by the first element of the canonical range list (which has the smallest lower bound), cached at construction time.
>>>belowRanges (5 +=+ 10 <> 20 +=+ 30 :: Ranges Integer) 1True>>>belowRanges (ubi 10 <> 20 +=+ 30 :: Ranges Integer) 1False
Set operations
mergeRanges :: Ord a => [Range a] -> Ranges a Source #
Canonicalise a raw list of Range values into a Ranges. Overlapping
ranges are merged; the result is sorted and non-overlapping.
>>>mergeRanges [LowerBoundRange (Bound 12 Inclusive), SpanRange (Bound 1 Inclusive) (Bound 10 Inclusive), SpanRange (Bound 5 Inclusive) (Bound 15 Inclusive) :: Range Integer]Ranges [lbi 1]
union :: Ord a => Ranges a -> Ranges a -> Ranges a Source #
Set union. Equivalent to (.<>)
>>>union (1 +=+ 10) (5 +=+ 15 :: Ranges Integer)Ranges [1 +=+ 15]
intersection :: Ord a => Ranges a -> Ranges a -> Ranges a Source #
Set intersection. Returns only values present in both.
>>>intersection (1 +=+ 10) (5 +=+ 15 :: Ranges Integer)Ranges [5 +=+ 10]
difference :: Ord a => Ranges a -> Ranges a -> Ranges a Source #
Set difference: values in the first Ranges not in the second.
>>>difference (1 +=+ 10) (5 +=+ 15 :: Ranges Integer)Ranges [1 +=* 5]
Enumerable methods
fromRanges :: (Ord a, Enum a) => Ranges a -> [a] Source #
Instantiate all values covered by the ranges as a list.
Warning: not efficient. Prefer inRanges for membership tests.
Combine with take to avoid evaluating infinite ranges.
>>>take 5 . fromRanges $ (1 +=+ 10 :: Ranges Integer)[1,2,3,4,5]
>>>take 6 . fromRanges $ (1 +=+ 3 :: Ranges Integer) <> (10 +=+ 12)[1,10,2,11,3,12]
joinRanges :: (Ord a, Enum a) => Ranges a -> Ranges a Source #
Join adjacent ranges that are contiguous for Enum types.
For example, [1 +=+ 5, 6 +=+ 10] collapses to [1 +=+ 10] for
Integer because there is no integer between 5 and 6.
>>>joinRanges (mconcat [1 +=+ 5, 6 +=+ 10] :: Ranges Integer)Ranges [1 +=+ 10]