通过拆分元素(并省略该元素)将列表变成列表的列表
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]
,我想把它变成只包含 Sunny
或 Cloudy
天的列表列表,如下所示:
[[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)
我有一个 data Weather = Sunny | Cloudy | Rainy deriving (Show, Eq)
数据类型,我的工作是使用 [Weather] -> [[Weather]]
函数仅对非雨天进行分组。
假设我有一个这样的列表:
[Rainy, Cloudy, Sunny, Sunny, Cloudy, Rainy, Rainy, Sunny, Cloudy, Sunny, Cloudy, Rainy, Sunny]
,我想把它变成只包含 Sunny
或 Cloudy
天的列表列表,如下所示:
[[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)