Haskell filtering a nested list with specific data constructors

拜拜、爱过 提交于 2019-12-31 07:43:52

问题


Suppose I have the data type

data Joke = Funny String | Lame String

and say I have the following nested list

[[Funny "Haha", Lame "boo"], [Funny "Haha"], [Lame "BOO"]]

How would I go about filtering such a nested list so that any list within the nested list that contains Funny "Haha" is removed? In other words, I'm trying to filter the list so that I receive the following result:

[[Lame "BOO"]] 

Any list that contains Funny "Haha" is removed.

Would appreciate any help, I'm having a terrible time learning Haskell and how to do things with lists and custom data types.


回答1:


As a preliminary step, let's derive Eq and Show instances for your type:

data Joke = Funny String | Lame String
    deriving (Eq, Show)

Eq makes it possible to test the (in)equality of Jokes, while Show makes it possible to display the results of your tests in GHCi.

filter is the right tool here:

excludeHaha :: [[Joke]] -> [[Joke]]
excludeHaha = filter (notElem (Funny "Haha"))

This filters the (outer) list so that only inner lists with all elements different from Funny "Haha" are kept.

Trying it out:

GHCi> excludeHaha [[Funny "Haha", Lame "boo"], [Funny "Haha"], [Lame "BOO"]]
[[Lame "BOO"]]

If you don't want to hardcode "Haha", add a parameter and pass it on to the test:

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"]]

If you wanted instead to exclude all Funny jokes, regardless of what the contained String is, you could use pattern matching to define an appropriate test:

excludeFunny :: [[Joke]] -> [[Joke]]
excludeFunny = filter (all (not . isFunny))
    where
    isFunny :: Joke -> Bool
    isFunny joke = case joke of
        Funny _ -> True
        _ -> False

The filtering test here uses all, which applies its own test (here, (/= Funny "Haha")) to the elements of the (inner) list and then folds the resulting [Bool] with (&&).

(You might as well define isLame and use it instead of not . isFunny -- note, though, that that would only work because you happen to have just two constructors.)

GHCi> excludeFunny [[Funny "LOL", Lame "boo"], [Funny "Haha"], [Lame "BOO"]]
[[Lame "BOO"]]

P.S.: A manual implementation of the Eq instance would look like this:

instance Eq Joke where
    Funny x == Funny y = x == y
    Lame x == Lame y = x == y
    Funny _ == Lame _ = False
    Lame _ == Funny _ = False

You might replace the last two cases by a catch-all (_ == _ = False), saving one line at the cost of making it a little easier to forget updating the instance if you ever add more constructors to Joke. In any case, such definitions are boring busywork, so we avoid it with deriving unless there is a need for a non-obvious equality test.



来源:https://stackoverflow.com/questions/48739022/haskell-filtering-a-nested-list-with-specific-data-constructors

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!