Haskell 如果给定一个表示范围的元组列表,如何合并连续范围?

Haskell 如果给定一个表示范围的元组列表,如何合并连续范围?,haskell,Haskell,如果给定一个表示如下范围的元组列表: [(0,10),(10,100),(1000,5000)] example :: RangeMap Int Bool example = Split 1000 (Split 100 (Split 0 (Leaf False) (Leaf False)) (Leaf False)) (Split 5000 (Leaf True) (Leaf False

如果给定一个表示如下范围的元组列表:

[(0,10),(10,100),(1000,5000)]
example :: RangeMap Int Bool
example = Split 1000 (Split 100 (Split 0 (Leaf False) (Leaf False))
                                (Leaf False)) 
                     (Split 5000 (Leaf True) (Leaf False))
我想合并表示连续范围的元组,结果如下:

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

编辑:范围是不重叠的

好吧,我认为这个领域最好的解决方案可能会涉及到维护所讨论的不变量的专门数据结构。在Java的土地上,Guava图书馆有,它正是这样做的

这并不能直接解决您的问题,但有一次我将“历史值”的简单(太简单)实现作为一种二进制搜索树:

-- | 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
与我过去在类似应用程序中使用的
[(因为,直到,值)]
表示法相比,这有一些巧妙之处:

  • 树表示法使得在相同的时间范围内不可能有冲突的
    a
    RangeMap
    是从
    t
    a
    的真正函数
  • 树表示保证将一些
    a
    分配给每个
    t
    。同样,
    RangeMap
    是从
    t
    a
    的真正函数
  • 因为它是一个树而不是一个列表,所以它支持日志时间操作

  • 我并没有为它设计一个平衡的树表示,也没有设计出如何合并具有相同值的相邻范围,但是…

    除非这是一个在您的程序中经常出现的模式,否则我只会选择直接递归(下面是未经测试的代码!):

    (在这里,您可以通过使用
    @
    -模式来优化一点,但要以混乱为代价)

    但是如果你真的想,你可以使用下面的helper函数

     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
    是否在某个库中,因为它对于手头的问题非常具体。

    我本以为mergeRanges[(0,10),(10,100),(10,1000),(1000,5000)]应该给出[(0,5000)],但在您的解决方案中,它给出了[(0,100),(10,5000)],因为每个范围只与一个其他范围合并。
     merge2Ranges (lo1, hi1) (lo2, hi2) 
         | hi1 == lo2 = Just (lo1, hi2)
         | otherwise = Nothing