2

当我尝试学习 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)]

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

4

3 回答 3

6

The general list comprehensions scheme for these things is

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

pred is a predicate acting on elements of xs, the input list; only for those that pass the test, their original indices are included in the output. Of course this can be done with higher order functions, as

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

(.) is the function composition operator: pred . fst == (\p -> pred (fst p)). Thus the above line could also be written

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

whatever is more readable to you.

update: filter is also implementable as

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

so the mapping can be fused in, giving us

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

concatMap can also be replaced with foldMap, join . map ... and even asum . map ....

于 2013-08-08T15:41:17.700 回答
2

正如其他帖子中提到的,您可以使用该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)

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

于 2013-08-08T18:33:39.503 回答
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]

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

于 2013-08-08T15:06:54.840 回答