@@ -16,6 +16,8 @@ module Data.Map
1616 , lookupGT
1717 , findMin
1818 , findMax
19+ , foldSubmap
20+ , submap
1921 , fromFoldable
2022 , fromFoldableWith
2123 , toUnfoldable
@@ -44,7 +46,7 @@ import Data.Foldable (foldl, foldMap, foldr, class Foldable)
4446import Data.List (List (..), (:), length , nub )
4547import Data.List.Lazy as LL
4648import Data.Maybe (Maybe (..), maybe , isJust , fromMaybe )
47- import Data.Monoid (class Monoid )
49+ import Data.Monoid (class Monoid , mempty )
4850import Data.Ord (class Ord1 )
4951import Data.Traversable (traverse , class Traversable )
5052import Data.Tuple (Tuple (Tuple), snd , uncurry )
@@ -254,6 +256,109 @@ findMin Leaf = Nothing
254256findMin (Two left k1 v1 _) = Just $ fromMaybe { key: k1, value: v1 } $ findMin left
255257findMin (Three left k1 v1 _ _ _ _) = Just $ fromMaybe { key: k1, value: v1 } $ findMin left
256258
259+ -- | Fold over the entries of a given map where the key is between a lower and
260+ -- | an upper bound. Passing `Nothing` as either the lower or upper bound
261+ -- | argument means that the fold has no lower or upper bound, i.e. the fold
262+ -- | starts from (or ends with) the smallest (or largest) key in the map.
263+ -- |
264+ -- | ```purescript
265+ -- | foldSubmap (Just 1) (Just 2) (\_ v -> [v])
266+ -- | (fromFoldable [Tuple 0 "zero", Tuple 1 "one", Tuple 2 "two", Tuple 3 "three"])
267+ -- | == ["one", "two"]
268+ -- |
269+ -- | foldSubmap Nothing (Just 2) (\_ v -> [v])
270+ -- | (fromFoldable [Tuple 0 "zero", Tuple 1 "one", Tuple 2 "two", Tuple 3 "three"])
271+ -- | == ["zero", "one", "two"]
272+ -- | ```
273+ foldSubmap :: forall k v m . Ord k => Monoid m => Maybe k -> Maybe k -> (k -> v -> m ) -> Map k v -> m
274+ foldSubmap kmin kmax f =
275+ let
276+ tooSmall =
277+ case kmin of
278+ Just kmin' ->
279+ \k -> k < kmin'
280+ Nothing ->
281+ const false
282+
283+ tooLarge =
284+ case kmax of
285+ Just kmax' ->
286+ \k -> k > kmax'
287+ Nothing ->
288+ const false
289+
290+ inBounds =
291+ case kmin, kmax of
292+ Just kmin', Just kmax' ->
293+ \k -> kmin' <= k && k <= kmax'
294+ Just kmin', Nothing ->
295+ \k -> kmin' <= k
296+ Nothing , Just kmax' ->
297+ \k -> k <= kmax'
298+ Nothing , Nothing ->
299+ const true
300+
301+ -- We can take advantage of the invariants of the tree structure to reduce
302+ -- the amount of work we need to do. For example, in the following tree:
303+ --
304+ -- [2][4]
305+ -- / | \
306+ -- / | \
307+ -- [1] [3] [5]
308+ --
309+ -- If we are given a lower bound of 3, we do not need to inspect the left
310+ -- subtree, because we know that every entry in it is less than or equal to
311+ -- 2. Similarly, if we are given a lower bound of 5, we do not need to
312+ -- inspect the central subtree, because we know that every entry in it must
313+ -- be less than or equal to 4.
314+ --
315+ -- Unfortunately we cannot extract `if cond then x else mempty` into a
316+ -- function because of strictness.
317+ go = case _ of
318+ Leaf ->
319+ mempty
320+ Two left k v right ->
321+ (if tooSmall k then mempty else go left)
322+ <> (if inBounds k then f k v else mempty)
323+ <> (if tooLarge k then mempty else go right)
324+ Three left k1 v1 mid k2 v2 right ->
325+ (if tooSmall k1 then mempty else go left)
326+ <> (if inBounds k1 then f k1 v1 else mempty)
327+ <> (if tooSmall k2 || tooLarge k1 then mempty else go mid)
328+ <> (if inBounds k2 then f k2 v2 else mempty)
329+ <> (if tooLarge k2 then mempty else go right)
330+ in
331+ go
332+
333+ -- | Returns a new map containing all entries of the given map which lie
334+ -- | between a given lower and upper bound, treating `Nothing` as no bound i.e.
335+ -- | including the smallest (or largest) key in the map, no matter how small
336+ -- | (or large) it is. For example:
337+ -- |
338+ -- | ```purescript
339+ -- | submap (Just 1) (Just 2)
340+ -- | (fromFoldable [Tuple 0 "zero", Tuple 1 "one", Tuple 2 "two", Tuple 3 "three"])
341+ -- | == fromFoldable [Tuple 1 "one", Tuple 2 "two"]
342+ -- |
343+ -- | submap Nothing (Just 2)
344+ -- | (fromFoldable [Tuple 0 "zero", Tuple 1 "one", Tuple 2 "two", Tuple 3 "three"])
345+ -- | == fromFoldable [Tuple 0 "zero", Tuple 1 "one", Tuple 2 "two"]
346+ -- | ```
347+ -- |
348+ -- | The function is entirely specified by the following
349+ -- | property:
350+ -- |
351+ -- | ```purescript
352+ -- | Given any m :: Map k v, mmin :: Maybe k, mmax :: Maybe k, key :: k,
353+ -- | let m' = submap mmin mmax m in
354+ -- | if (maybe true (\min -> min <= key) mmin &&
355+ -- | maybe true (\max -> max >= key) mmax)
356+ -- | then lookup key m == lookup key m'
357+ -- | else not (member key m')
358+ -- | ```
359+ submap :: forall k v . Ord k => Maybe k -> Maybe k -> Map k v -> Map k v
360+ submap kmin kmax = foldSubmap kmin kmax singleton
361+
257362-- | Test if a key is a member of a map
258363member :: forall k v . Ord k => k -> Map k v -> Boolean
259364member k m = isJust (k `lookup` m)
0 commit comments