【问题标题】:Haskell List ComprehensionsHaskell 列表理解
【发布时间】:2013-08-08 15:03:17
【问题描述】:

我在尝试学习 Haskell 时尝试做一个 haskell 问题。

这个问题给了我以下类型定义:
type Word = String
type Line = [Word]
type Book = [Line]

然后问题要求我定义一个函数 index :: Word -> Book -> [Int] 它接受一个单词和一本书,并返回单词出现的行号。例如:
index "example" [["example", "town"], ["example", "cat", "this"]] = [1,2]

到目前为止,我已经使用 zip book [1 .. length book] 将行号附加到每一行,这样就可以了

[(["example","town"],1),(["example","cat","this"],2)]

然后我将如何仅提取行号?我假设我会使用列表推导,但我不知道该怎么做。

【问题讨论】:

    标签: list haskell list-comprehension


    【解决方案1】:

    这些东西的一般列表理解方案是

    g xs = [i | (x,i) <- zip xs [1..], pred x]
    

    pred 是作用于输入列表xs 的元素的谓词;只有那些通过测试的,他们的原始索引才会包含在输出中。当然,这可以通过高阶函数来完成,如

    g xs = map snd . filter (pred . fst) . (`zip` [1..]) $ xs
    

    (.) 是函数组合运算符:pred . fst == (\p -&gt; pred (fst p))。因此上面的行也可以写成

    g xs = map snd . filter (\(x,i) -> pred x) . (`zip` [1..]) $ xs
    

    任何对你来说更易读的东西。

    更新: filter 也可以实现为

    filter pred = concatMap (\x -> [x | pred x])
    

    所以映射可以融合进去,给我们

    g :: (a -> Bool) -> [a] -> [Int]
    g pred = concatMap (\(x,i) -> [i | pred x]) . (`zip` [1..])
    

    concatMap也可以替换为foldMapjoin . map ...甚至asum . map ...

    【讨论】:

    • 另一个问题(我不想开一个新的,而且SO中不存在PM):
    • 列表理解更高效还是更高阶的函数实现?
    • @kaan:如果你打开优化,它们都会编译成相同的代码,所以选择更适合你的。
    【解决方案2】:

    您可以使用map,它将一个函数应用于列表中的每个元素。您要应用的函数是snd,它提取第二个元素。

    λ> let xs = [(["example","town"],1),(["example","cat","this"],2)]
    λ> map snd xs
    [1,2]
    

    您可能希望向我们展示您的其余代码。您提到您使用了Tzip book [1 .. length book]——通常有比使用length 函数更简单、更有效的方法。我们也许可以建议一种更“Haskellish”的方式。

    编辑:过滤

    我们可以通过编写一个简单的函数来过滤列表,以查找包含我们感兴趣的单词的所有条目。在下面的示例中,我为此定义了containsWord

    λ> let xs = [(["example","town"],1),(["example","cat","this"],2)]
    λ> let containsWord w (ws,_) = w `elem` ws
    λ> let ys = filter (containsWord "cat") xs
    λ> map snd ys
    [2]
    

    或者,如果你想内联定义函数:

    λ> let xs = [(["example","town"],1),(["example","cat","this"],2)]
    λ> let ys = filter (\(zs,_) -> "cat" `elem` zs) xs
    λ> map snd ys
    [2]
    

    您当然可以对所有这些东西使用列表推导。但是您可能会发现使用 mapfilter 之类的函数会产生更易读的代码。如果您在几个月后回顾您的代码,您将立即理解 mapfilter 的用途,但需要更仔细地检查才能弄清楚列表理解实际上在做什么 >。我更喜欢仅在熟悉的函数尚未涵盖的情况下使用列表推导。

    【讨论】:

    • 谢谢,但是如何过滤列表?我可以在应用 zip 函数之前轻松过滤列表,但在应用它之后它不允许我过滤,因为一个整数出现在列表中每个元素的末尾。但是,如果我在压缩之前过滤,这意味着行号不正确。例如,如果我过滤“cat”然后压缩,它会给我 1 作为行号
    【解决方案3】:

    正如其他帖子中提到的,您可以使用zip 函数来用行号装饰每一行。然后,您可以使用列表推导式进行搜索:

    search :: Word -> Book -> [Int]  
    search w b =  
      [n | (line, n) <- lines, elem w line]  
      where lines = zip b [1..]
    

    您也可以使用foldl 定义函数(虽然不确定它的风格是否好,我还是 Haskell 的初学者):

    search :: Word -> Book -> [Int]
    search w b =
      fst $ foldl foo ([], 1) b
      where foo (rs, n) line | elem w line = (n : rs, n+1)
                             | otherwise = (rs, n+1)
    

    您也可以递归地定义它,等等。希望它有所帮助!

    【讨论】:

    • 一个。您正在重新创建现有函数 filter;湾。你用左折叠来做到这一点,从而引入了不必要的严格性,例如它不适用于无限列表,因为 foldl always 会消耗整个输入列表; C。结果列表以相反的顺序构建; d。但是您已经有了在遍历列表时传递状态的想法,看看您是否可以使其与unfoldriterate 一起工作。 :)
    • 另外,foldr (\line next i -&gt; if (elem w line) then i:next (i+1) else next (i+1)) (const []) book 1。 :)
    猜你喜欢
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 2015-03-10
    • 2018-04-27
    • 2021-01-12
    • 2019-08-20
    相关资源
    最近更新 更多