Haskell用函数迭代遍历列表

时间:2017-11-09 16:33:03

标签: haskell functional-programming

我遇到了一些我刚写的Haskell代码问题。我只是一个初学者,所以我不太了解Haskell的语法,现在这个错误刚刚出现,我不知道如何摆脱它。

  

无法将预期类型'[Bool]'与实际类型'Bool'匹配

 grep :: String -> IO()
 grep x = do
   fileContent <- readFile "/home/User/Desktop/foo.txt"
   let fileRows = lines fileContent
   let rowNumbers = -1
   if (map (isInfixOf x) fileRows == True) 
   then do let rowNumbers2 = rowNumbers
           let rowNumbers = rowNumbers2+1
           putStrLn ("[" ++ show(rowNumbers) ++ "] : " ++ x)
   else do let rowNumbers2 = rowNumbers
           let rowNumbers = rowNumbers2+1
           print "False"

我想检查fileContent是否包含单词x。如果是,我想打印行和行号,其他我想继续搜索,直到我到达文件的末尾。如何迭代fileRows来检查单词是否在那里?

2 个答案:

答案 0 :(得分:2)

这是一种更好的方式来做你想要完成的事情:

我们开始像以前一样在文件中读取,但是fmap在它上面划线,所以我们不需要使用另一个变量。然后我们使用zipWith来运行contains函数。它需要我们要重写的字符串,一行和一个行号。

行号来自[1 ..]部分。这是无限列表的语法糖,从1开始并永远增加1,所以[1,2,3 ..] 因为zipWith需要2个列表,所以它只需要从这个无限列表中获取所需的内容。

它检查该行是否存在,如果是,则创建该字符串,如果不存在则返回Nothing。然后我们调用catMaybes,它接受​​一个maybes列表,并返回所有正确的值。

现在我们有包含grepped字符串的每一行的输出字符串,所以我们只需要映射列表并打印它。 mapM_就像map一样,除了它采取monadic动作,将它应用于列表中的每个项目,并丢弃结果。

通过拆分这样的函数,我们也可以创建一个很好的纯函数,而不是在没有真正需要时使用一个长IO函数。

import Data.List (isInfixOf)
import Data.Maybe (catMaybes)

grep x = do
    fileContent <- lines <$> readFile "foo.txt"
    let result = catMaybes $ zipWith (contains x) fileContent [1..]
    mapM_ putStrLn result

contains x y lineNum
    | x `isInfixOf` y = Just $ "[" ++ show lineNum ++ "] : " ++ y
    | otherwise = Nothing

给定文件foo.txt:

apple
banana
orange
apple banana
orange cherry
cherry apple

输出:

λ> grep "apple"
[1] : apple
[4] : apple banana
[6] : cherry apple
λ> grep "foo"
λ> grep "orange"
[3] : orange
[5] : orange cherry
λ> 

答案 1 :(得分:1)

在这一行中,您将函数映射到fileRows,因此您将返回一个Bool数组。

map (isInfixOf x) fileRows == True

您要做的是测试任何行是否包含x。如果您使用any而不是map,它应该可以工作。

any (isInfixOf x) fileRows == True