如果给出表示范围的元组列表,您如何合并连续范围?

时间:2014-11-11 22:59:09

标签: haskell

如果给出一个代表范围的元组列表:

[(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

编辑:范围不重叠

2 个答案:

答案 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)]表示相比,这有一些巧妙的事情:

  1. 树表示使得无法在相同的时间范围内具有冲突的a值。 RangeMap是从ta的真正功能。
  2. 树表示保证为每个a分配一些t。同样,RangeMap是从ta的真实函数。
  3. 由于它是树而不是列表,因此它支持日志时间操作。
  4. 我没有为此计算出平衡的树表示,或者计算出如何将相邻的范围合并到相同的值,但是...