patternMinor
Parsing n lines to count vowels - HackerEarth
Viewed 0 times
vowelsparsingcounthackerearthlines
Problem
I have written a haskell program for the following 'Code Monk' Challenge at HackerEarth.
Here is the challenge description. Basically, we are looking for the number of vowels in a string. The first input n is the number of string to parse, followed by n number of random strings.
And here is my implementation:
My code is working correctly but as I'm fairly new to functional programming and Haskell I would like some comments about my code. More specifically, I would like the
Here is the challenge description. Basically, we are looking for the number of vowels in a string. The first input n is the number of string to parse, followed by n number of random strings.
And here is my implementation:
import Data.Char
main = do
numberOfTestCases Int
countVowels line = length $ filter (`elem` ['a','e','i','o','u']) $ map toLower lineMy code is working correctly but as I'm fairly new to functional programming and Haskell I would like some comments about my code. More specifically, I would like the
processLine function to not have a side effect, but I don't know how can I do that. Any other tips or comments would be greatly appreciated!Solution
Type signatures
You should add type signatures to all top-level bindings.
But that's not what you've intended. Therefore, make sure to add type-signatures to all your top-level bindings:
By the way,
Use syntactic sugar
By the way, if you use
Keeping things pure(?)
More specifically, I would like the processLine function to not have a side effect, but I don't know how can I do that.
At some point, you need to read the line. You've chosen
Still, we can have a look at
Or we can interpret it as
If we have a look at the modules in
Note that
Keeping things actually pure
Alright, we've seen how one could use standard functions to process all those lines. But it's still in
Well, what would the alternative be? We still need to process a bunch of lines. A "bunch of something" can be easily expressed with a list, so let us write the type signature of our potential candidate.
The implementation comes rather easy:
Now we just need to get a list of strings. Since we now want to get something from the user, we have to use
Seems familiar, doesn't it? "repeat-an-action-n-times". This time, it's
We're now missing two last ingredients. First, we need to print a list of
Exercise: write
We end up with the following
Further remarks
Search some of the
For completion, here's how I would (probably) solve the challenge:
You should add type signatures to all top-level bindings.
processLine's returned value and main should have the same type, which can be checked with the type checker, if you add type signatures. The following code would compile, since main will have the type IO Int:processLine 0 = return 0But that's not what you've intended. Therefore, make sure to add type-signatures to all your top-level bindings:
main :: IO ()
main = …
processLine :: Int -> IO ()
processLine n = …By the way,
processLine's type signature makes n's type unambigous, and therefore you don't need an explicit type in (read numberOfTestCases :: Int) anymore.Use syntactic sugar
['a','e','i','o','u'] is "aeiou", which is easier to change and to read (in my opionion). That way, we get:countVowels :: String -> Int
countVowels line = length $ filter (`elem` "aeiou") $ map toLower lineBy the way, if you use
"aeiouAEIOU", you don't need to import Data.Char.Keeping things pure(?)
More specifically, I would like the processLine function to not have a side effect, but I don't know how can I do that.
At some point, you need to read the line. You've chosen
processLine to do so. Note that it is not feasible to remove IO from processLine, because you would end up with processLine = countVowels.Still, we can have a look at
processLine to see what we can do. We can read the code literally:- If
nis 0, do nothing
- If
nis greater than zero, do
- read a line
- count the vowels of the line
- print the number of vowels
- call
processLinewithndecreased by one
Or we can interpret it as
- repeat the following action
ntimes:
- read a line
- count the vowels of the line
- print the number of vowels
- return nothing
If we have a look at the modules in
base, we'll notice that Control.Monad contains replicateM_, which does exactly the "repeat-n-times"-part:processAllLines :: Int -> IO ()
processAllLines n = replicateM_ n processSingleLine
processSingleLine :: IO ()
processSingleLine = do
line <- getLine
print $ countVowels lineNote that
processSingleLine can get shortened into getLine >>= print . countVowels, but I'm not sure whether you're ready for that yet.Keeping things actually pure
Alright, we've seen how one could use standard functions to process all those lines. But it's still in
IO. Can't we do anything?Well, what would the alternative be? We still need to process a bunch of lines. A "bunch of something" can be easily expressed with a list, so let us write the type signature of our potential candidate.
processList :: [String] -> [Int]The implementation comes rather easy:
processList = map countVowelsNow we just need to get a list of strings. Since we now want to get something from the user, we have to use
IO:getUserLines :: Int -> IO [String]Seems familiar, doesn't it? "repeat-an-action-n-times". This time, it's
getLine. Since we don't want to throw the result away, we use replicateM (note the missing _):getUserLines n = replicateM n getLineWe're now missing two last ingredients. First, we need to print a list of
Int so that the challenged site accepts it:printSolutions :: [Int] -> IO ()Exercise: write
printSolution. Note that there is a function that might help you in the process.We end up with the following
main:main :: IO ()
main = do
numberOfTestCases <- readLn
userLines <- getUserLines numberOfTestCases
printSolutions (processList userLines)Further remarks
Search some of the
base modules whether they provide the functions you're looking for. And have a look at Prelude. You've used read after getLine, which is provided as a single function readLn (see above).For completion, here's how I would (probably) solve the challenge:
import Control.Monad (replicateM_)
main :: IO ()
main = do
numberOfTest >= print . countVowels
countVowels :: String -> Int
countVowels xs = length $ filter (`elem` "aeuioAEUIO") xsCode Snippets
processLine 0 = return 0main :: IO ()
main = …
processLine :: Int -> IO ()
processLine n = …countVowels :: String -> Int
countVowels line = length $ filter (`elem` "aeiou") $ map toLower lineprocessAllLines :: Int -> IO ()
processAllLines n = replicateM_ n processSingleLine
processSingleLine :: IO ()
processSingleLine = do
line <- getLine
print $ countVowels lineprocessList :: [String] -> [Int]Context
StackExchange Code Review Q#160369, answer score: 2
Revisions (0)
No revisions yet.