Simon
Simon

Reputation: 19

Haskell iterate throught list with function

I am having an issue with some Haskell code I just wrote. I am just a beginner so I don't know much about the syntax of Haskell and now this error just popped up and I have no clue how to get rid of it.

Couldn't match expected type ‘[Bool]’ with actual type ‘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"

I want to check if fileContent contains the word x. If yes, I want to print the line and the line number, otheriwse I want to keep searching until I reach the end of the file. How can I iterate through fileRows to check if the word is in there?

Upvotes: 0

Views: 340

Answers (2)

Zpalmtree
Zpalmtree

Reputation: 1359

Here's a more haskelly way to do what you're trying to accomplish:

We start by reading in the file as before, but fmap lines over it so we don't need to use another variable. Then we use zipWith to run the contains function. It takes the string we're grepping for, a line, and a line number.

The line number is from the [1..] section. This is syntactic sugar for an infinite list, starting at 1 and increasing by 1 forever, so [1,2,3..] Because zipWith takes 2 lists, it only takes as much as it needs from this infinite list.

It checks if the line is present, and if it is, creates the string, and if not returns Nothing. Then we call catMaybes, which takes a list of maybes, and returns all the just values.

Now we have the output string for every line that contains the grepped string, so we just need to map over the list and print it. mapM_ is like map, except it takes a monadic action, applies it to each item in the list, and discards the results.

By splitting up the functions like this, we also get to make a nice pure function instead of having one long IO function when it's not really needed.

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

Given a file foo.txt:

apple
banana
orange
apple banana
orange cherry
cherry apple

It outputs:

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

Upvotes: 2

Axnyff
Axnyff

Reputation: 9954

In this line, you are mapping a function over fileRows, so you're returning an array of Bool.

map (isInfixOf x) fileRows == True

What you want to do is test if any row contains x. If you use any instead of map, it should work.

any (isInfixOf x) fileRows == True

Upvotes: 1

Related Questions