【问题标题】:Haskell filtering a nested list with specific data constructorsHaskell 使用特定的数据构造函数过滤嵌套列表
【发布时间】:2018-07-22 04:23:40
【问题描述】:

假设我有数据类型

data Joke = Funny String | Lame String

并说我有以下嵌套列表

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

我将如何过滤这样一个嵌套列表,以便删除嵌套列表中包含Funny "Haha" 的任何列表?换句话说,我正在尝试过滤列表,以便收到以下结果:

[[Lame "BOO"]] 

任何包含Funny "Haha" 的列表都会被删除。

不胜感激,我在学习 Haskell 以及如何使用列表和自定义数据类型做事时遇到了困难。

【问题讨论】:

    标签: list haskell recursion functional-programming


    【解决方案1】:

    作为初步步骤,让我们为您的类型派生 EqShow 实例:

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

    Eq 可以测试Jokes 的(不)相等性,而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](&&) 折叠起来。

    (您不妨定义 isLame 并使用它而不是 not . isFunny ——但请注意,这只会起作用,因为您恰好只有两个构造函数。)

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

    P.S.:Eq 实例的手动实现如下所示:

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

    你可以用一个包罗万象的 (_ == _ = False) 替换最后两种情况,如果你向Joke 添加更多构造函数,则可以更容易忘记更新实例的代价节省一行。无论如何,这样的定义都是无聊的工作,所以我们避免使用deriving,除非需要进行不明显的相等测试。

    【讨论】:

    • 如果不导出 Eq,我该怎么做?此外,对于模式匹配,您编写了函数 excludeHaha = filter (all (/= Funny "Haha"))。但是,您对值“哈哈”进行了硬编码,一般情况下我该怎么做?例如,如果字符串表示“X”,该函数将销毁所有有趣的“X”列表。或者如果字符串是“Y”,该函数将销毁所有包含“Y”的列表。
    • @Lol 我也将其添加到答案中。
    • 我想我想做的是:给定一个嵌套列表,检查第一个列表,然后检查第一个元素。如果是Funny,则删除嵌套列表中包含Funny x 的所有列表,并返回更新后的嵌套列表。但问题是用户只输入 [[Joke]],所以我从来没有明确地给出一个字符串 s。
    • @Lol 我不确定你对最后一部分的意思——你事先知道你会给excludeAFunnyJoke 的字符串吗?
    • 一个字符串 x*****
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-09
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    相关资源
    最近更新 更多