假设我有数据类型
data Joke = Funny String | Lame String
并说我有以下嵌套列表
[[Funny "Haha", Lame "boo"], [Funny "Haha"], [Lame "BOO"]]
如何过滤这样的嵌套列表,以便删除包含Funny "Haha"
的嵌套列表中的任何列表?换句话说,我正在尝试过滤列表,以便收到以下结果:
[[Lame "BOO"]]
删除包含Funny "Haha"
的所有列表。
感谢任何帮助,我在学习Haskell以及如何使用列表和自定义数据类型时遇到了很多困难。
答案 0 :(得分:2)
作为初步步骤,让我们为您的类型派生Eq
和Show
个实例:
data Joke = Funny String | Lame String
deriving (Eq, Show)
Eq
可以测试Joke
s的(in)相等性,而Show
则可以在GHCi中显示测试结果。
filter
是正确的工具:
excludeHaha :: [[Joke]] -> [[Joke]]
excludeHaha = filter (notElem (Funny "Haha"))
这会过滤(外部)列表,以便只保留包含与Funny "Haha"
不同的所有元素的内部列表。
尝试一下:
GHCi> excludeHaha [[Funny "Haha", Lame "boo"], [Funny "Haha"], [Lame "BOO"]]
[[Lame "BOO"]]
如果您不想硬编码"Haha"
,请添加参数并将其传递给测试:
excludeAFunnyJoke :: String -> [[Joke]] -> [[Joke]]
excludeAFunnyJoke s = filter (notElem (Funny s))
GHCi> excludeAFunnyJoke "LOL" [[Funny "LOL", Lame "boo"], [Funny "Haha"], [Lame "BOO"]]
[[Funny "Haha"],[Lame "BOO"]]
如果您想要排除所有Funny
个笑话,无论包含的String
是什么,您都可以使用模式匹配来定义适当的测试:
excludeFunny :: [[Joke]] -> [[Joke]]
excludeFunny = filter (all (not . isFunny))
where
isFunny :: Joke -> Bool
isFunny joke = case joke of
Funny _ -> True
_ -> False
此处的过滤测试使用all
,它将自己的测试(此处为(/= Funny "Haha")
)应用于(内部)列表的元素,然后将结果[Bool]
折叠为{{ 1}}。
(您也可以定义(&&)
并使用它而不是isLame
- 请注意,这只会起作用,因为您碰巧只有两个构造函数。)
not . isFunny
P.S。:GHCi> excludeFunny [[Funny "LOL", Lame "boo"], [Funny "Haha"], [Lame "BOO"]]
[[Lame "BOO"]]
实例的手动实现如下所示:
Eq
你可以用catch-all(instance Eq Joke where
Funny x == Funny y = x == y
Lame x == Lame y = x == y
Funny _ == Lame _ = False
Lame _ == Funny _ = False
)替换最后两种情况,保存一行代价是为了让你更容易忘记更新实例,如果你有更多的构造函数添加到{{1 }}。在任何情况下,这样的定义都很繁琐,所以我们用_ == _ = False
来避免它,除非需要进行非显而易见的相等测试。