如果给出一个代表范围的元组列表:
[(0,10),(10,100),(1000,5000)]
我想合并代表连续范围的元组,结果如下:
[(0,100),(1000,5000)]
任何优雅的解决方案?
这是我的
mergeRanges :: [(Int, Int)] -> [(Int, Int)]
mergeRanges xs = foldr f [] (sort xs)
where f new@(x,y) acc@((a,b):ys) =
if y == a
then (x,b):ys
else new:acc
f x acc = x:acc
编辑:范围不重叠
答案 0 :(得分:3)
除非这是一个在你的程序中更频繁出现的模式,否则我只会进行直接递归(未经测试的代码如下!):
mergeRanges (lo1,hi1) : (lo2,hi2) : rest)
| hi1 == lo2 = mergeRanges ((lo1,hi2) : rest)
-- or (lo1,hi2) : mergeRanges rest, to merge only adjacent ranges
mergeRanges (interval:rest) = interval : mergeRanges rest
mergeRanges [] = []
(你可以使用@
- 模式来优化一点 - 以杂乱为代价)。
但如果你真的想,你可以使用以下帮助函数
merge :: (a -> a -> Maybe a) -> [a] -> [a]
merge f [] = []
merge f [x] = [x]
merge f (x:y:xs) = case f x y of
Nothing -> x : merge f (y:xs)
Just z -> merge (z:xs) -- or z : merge xs
并作为第一个参数
merge2Ranges (lo1, hi1) (lo2, hi2)
| hi1 == lo2 = Just (lo1, hi2)
| otherwise = Nothing
我怀疑merge
是否在某个库中,因为它对手头的问题非常具体。
答案 1 :(得分:0)
嗯,我认为这个领域的最佳解决方案可能涉及维护不变量的专用数据结构。在Java-land中,Guava库有RangeSet
,正是这样做。
这不是直接解决你的问题的方法,但是当我把这个简单(太简单)的“历史价值”实现作为一种二元搜索树时:
-- | A value that changes over time at discrete moments. @t@ is the timeline type,
-- @a@ is the value type.
data RangeMap t a = Leaf a
-- Invariant: all @t@ 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
分配一些t
。同样,RangeMap
是从t
到a
的真实函数。我没有为此计算出平衡的树表示,或者计算出如何将相邻的范围合并到相同的值,但是...