通过拆分元素(并省略该元素)将列表变成列表的列表

Making a list into a list of lists by splitting on an element (and omitting that element)

我有一个 data Weather = Sunny | Cloudy | Rainy deriving (Show, Eq) 数据类型,我的工作是使用 [Weather] -> [[Weather]] 函数仅对非雨天进行分组。

假设我有一个这样的列表:

[Rainy, Cloudy, Sunny, Sunny, Cloudy, Rainy, Rainy, Sunny, Cloudy, Sunny, Cloudy, Rainy, Sunny],我想把它变成只包含 SunnyCloudy 天的列表列表,如下所示:

[[Cloudy, Sunny, Sunny, Cloudy], [Sunny, Cloudy, Sunny, Cloudy], [Sunny]].

我被禁止使用 Data.List.Split 中的 splitOn 功能。

我尝试解决这个问题:

groupNotRainy :: [Weather] -> [[Weather]]
groupNotRainy [] = [[]]
groupNotRainy [x]
   | x /= Rainy = [[x]]
   | otherwise = [[]]
groupNotRainy [x,y]
   | x == Rainy = [[y]]
   | y == Rainy = [[x]]
   | otherwise = [[x,y]]
groupNotRainy (x:y:xs)
   | y == Rainy = [[x], groupNotRainy xs]

这给我一个 Couldn't match type ‘[Weather]’ with ‘Weather’ 错误。

您已接近解决方案。 为了进行类型检查,您的代码末尾应更改为:

groupNotRainy (x:y:xs)
   | y == Rainy  =  [x] : (groupNotRainy xs)
   | otherwise   =  ...TODO...

因为groupNotRainy xs的类型是[[Weather]].

您的以下模式恰好与空 xs 重叠:

groupNotRainy [x,y]
groupNotRainy (x:y:xs)

所以最好只保留第二个。在那里,您有 2 个布尔值要测试。我认为 easiest/cleanest(如果不是最短的话)方法只是枚举 4 个案例:

groupNotRainy [] = []
groupNotRainy [x]
   | x /= Rainy  =  [[x]]
   | otherwise   =  []
groupNotRainy (x:y:xs)
   | x == Rainy && y == Rainy         =  groupNotRainy xs
   | x /= Rainy && y == Rainy         =  [x] : (groupNotRainy xs)
   | x == Rainy && y /= Rainy         =  groupNotRainy (y:xs)
   | otherwise  {- both non-rainy -}  =  let  yss = groupNotRainy (y:xs)
                                         in   (x : head yss) : tail yss

如果 x 和 y 都正常(非下雨),则计算 y:xs 函数的结果,然后将 x 添加到该结果(必须非空)的第一个列表中。

请注意最后一个子句是 otherwise。编译器不是无所不知的,如果它只是 | (p x) && (p y).

就会抱怨

此外,定义一个通用函数可能更地道

grouper :: (a -> Bool) -> [a] -> [[a]]
grouper p []     = []
grouper p [x]
   | (p x)       =  [[x]]
   | otherwise   =  []
grouper p (x:y:xs)
   | (not $ p x)  &&  (not $ p y)   =  grouper p xs
   | (      p x)  &&  (not $ p y)   =  [x] : (grouper p xs)
   | (not $ p x)  &&  (      p y)   =  grouper p (y:xs)
   | otherwise  {- both x y OK -}   =  let  yss = grouper p (y:xs)  -- not empty
                                       in   (x : head yss) : tail yss

然后通过currying定义groupNotRainy,也称为“部分应用”:

groupNotRainy :: [Weather] -> [[Weather]]
groupNotRainy = grouper (/= Rainy)