Ну, я думаю, что лучшие решения в этом пространстве, вероятно, будет включать в себя специализированные структуры данных, которые поддерживают инварианта в вопросе. В Java-land библиотека Guava имеет RangeSet
, что делает именно это.
Это не решение вашей проблемы сразу, но когда-то я играл с этим простым (слишком простой) реализации «исторических ценностей» как своего рода бинарного дерева поиска:
-- | A value that changes over time at discrete moments. @[email protected] is the timeline type,
-- @[email protected] is the value type.
data RangeMap t a = Leaf a
-- Invariant: all @[email protected] values in the left branch must be less than
-- the one in the parent.
| Split t (RangeMap a) (RangeMap a)
valueAt :: RangeMap t a -> t -> a
valueAt _ (Leaf a) = a
valueAt t (Split t' before since)
| t < t' = get t before
| otherwise = get t since
Идея вот что Split t beforeT sinceT
делит временную шкалу на две ветви, одну для значений, которые были проведены до t
, и вторую для тех, которые удерживались с t
.
Так представлены в терминах этого типа, ваш заданный диапазон может быть представлен что-то вроде этого:
example :: RangeMap Int Bool
example = Split 1000 (Split 100 (Split 0 (Leaf False) (Leaf False))
(Leaf False))
(Split 5000 (Leaf True) (Leaf False))
Есть несколько аккуратных вещей об этом, по сравнению с [(since, until, value)]
представления, которое я использовал в прошлое для аналогичных приложений:
- Древовидное представление не позволяет иметь конфликтующие
a
значения за тот же диапазон времени. RangeMap
- это настоящая функция от t
до a
.
- Древовидное представление гарантирует, что для каждого присваивается
a
. Опять же, RangeMap
является истинной функцией от t
до a
.
- Поскольку это дерево, а не список, оно поддерживает операции входа в журнал.
я не пошел так далеко, как разработка сбалансированного представительства дерева для этого или выяснить, как объединить соседние диапазоны с тем же значением, однако ...
Я бы подумал, что mergeRanges [(0, 10), (10, 100), (10, 1000), (1000, 5000)] должен давать [(0, 5000)], но с вашим решением он дает [ (0, 100), (10, 5000)], поскольку каждый диапазон объединяется только с одним другим диапазоном. –