Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Take input as a string and print it 10 times using do-notation?
print10Times :: String -> IO ()
print10Times name = putStrLn $ repeat 10 name
main :: IO ()
main = do
putStrLn "What is your name?"
name <- getLine
print10Times name
You can use replicateM_ for this.
Λ: :t replicateM_
replicateM_ :: Monad m => Int -> m a -> m ()
Λ: do { putStrLn "what is your name?" ; name <- getLine ; replicateM_ 10 (putStrLn name) }
what is your name?
none of your business
none of your business
none of your business
none of your business
none of your business
none of your business
none of your business
none of your business
none of your business
none of your business
none of your business
repeat only takes a single argument and repeats it indefinitely. You are probably looking for take 10 $ repeat name.
If you want to print a list, you may be better off using mapM_ to map putStrLn against each element in the list. Try this out:
print10Times :: String -> IO ()
print10Times name = mapM_ putStrLn $ take 10 $ repeat name
Or, put more succinctly:
print10Times = mapM_ putStrLn . take 10 . repeat
You can definitely use replicateM_ like #pdexter suggests, or you can rewrite it using recursion:
print10Times :: String -> IO ()
print10Times = printNTimes 10
printNTimes :: Int -> String -> IO ()
printNTimes n name | n <= 0 = return ()
| otherwise = do
putStrLn name
printNTimes (n-1) name
main :: IO ()
main = do
putStrLn "What is your name?"
name <- getLine
print10Times name
print10Times simply redirects control to printNTimes with a constant 10 (the number of times it should be printed). Then a decrement is performed each time the name is printed and return () is done if the counter hits 0.
This is a bit reinventing replicateM_ which could be defined as:
replicateM_ :: (Monad m, Num n, Ord n) => n -> m a -> m ()
replicateM_ n f = loop n
where loop i | i <= 0 = return ()
| otherwise = f >> loop (i-1)
Related
nextState :: IO Int -> IO Int -- 0 1 0 2 0 1 0
nextState stateIO = do
value <- stateIO
putStrLn $ "Current state: " ++ show value
fmap (+1) stateIO
nextState' :: IO Int -> IO Int -- 0 1 2
nextState' stateIO = do
value <- stateIO
putStrLn $ "Current state: " ++ show value
return $ value + 1
main :: IO ()
main = do
let startStateIO = return 0 :: IO Int
let states = iterate nextState' startStateIO -- Use nextState or nextState'
stateInt <- states !! 3
print stateInt -- 3 in both cases
This Haskell code has 2 functions which both seemingly have identical behavior. However, printing calls shows that nextState is being called a lot more times than nextState'.
I had a larger project where this was an issue and I couldn't figure out how to convert that function so that it was called the minimum number of times so I couldn't fix it.
Why does this happen and how can I prevent it in a less simple example?
Note that fmap (+1) in my actual project is just a function from IO a -> IO a, not fmap (a -> a) - the whole thing works in terms of IO, not modifying the values inside using (a->a)
It should be easier to understand this example, which is analogous:
twice :: IO () -> IO ()
twice act = do
() <- act
fmap id act -- like what you did in `nextState`
once :: IO () -> IO ()
once act = do
() <- act
return $ id () -- like what you did in `nextState'`
...or shorter
twice :: IO () -> IO ()
twice act = act >> act
once :: IO () -> IO ()
once act = act
For example,
> twice (putStrLn "hello")
hello
hello
> once (putStrLn "hello")
hello
Iterating once doesn't do anything, because it's just the identity.
> iterate once (putStrLn "hello") !! 4
hello
Iterating twice, however...
Prelude> iterate twice (putStrLn "hello") !! 4
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
hello
Assume i have something like this
main = do
input_line <- getLine
let n = read input_line :: Int
replicateM n $ do
input_line <- getLine
let x = read input_line :: Int
return ()
***putStrLn $ show -- Can i access my replicateM here?
return ()
Can i access the result of my replicateM such as if it was a returned value, and for example print it out. Or do i have to work with the replicateM inside the actual do-block?
Specialized to IO
replicateM :: Int -> IO a -> IO [a]
which means that it returns a list. So in your example you could do:
results <- replicateM n $ do
input_line <- getLine
let x = read input_line :: Int
return x -- <- we have to return it if we want to access it
print results
replicateM n a returns a list of the values returned by a. In your case that'd just be a list of units because you have the return () at the end, but if you replace that with return x, you'll get a list of the read integers. You can then just use <- to get it out of the IO.
You can also simplify your code by using readLine instead of getLine and read. Similarly putStrLn . show can be replaced with print.
main = do
n <- readLn
ints <- replicateM n readLn :: IO [Int]
print ints
Of course. Its type is replicateM :: Monad m => Int -> m a -> m [a]. It means it can appear to the right of <- in a do block:
do
....
xs <- replicateM n $ do { ... }
....
xs will be of type [a], as usual for binding the results from Monad m => m [a].
With your code though, where you show return () in that nested do, you'll get ()s replicated n times in your xs. Presumably in the real code you will return something useful there.
There is the following task:
First line is the number of cases
For each case there is a line with the number of numbers to add
For each case there is also a line with the numbers
For each case I have to print summed numbers
Example:
Input:
2
5
1 2 3 4 5
2
-100 100
Output:
15
0
This is my implementation
import Control.Monad
main = do
linesCount <- readLn :: IO Int
numbers <- replicateM linesCount getCase
mapM_ putStrLn $ map (show.sum) numbers
getCase :: IO [Int]
getCase = do
numbersCount <- readLn :: IO Int -- actually I don't need this variable
numbersString <- getLine
let numbers = map read $ words numbersString
return numbers
It looks like a lot of code for parsing input. Are there any tricks to "compress" it? :)
If you merely want to make code shorter then check out the Stack Exchange community for code golfing. That is primarily for fun and games.
If we are thinking there is too much code it may not be that we need to make it shorter but rather that we need to make it clearer. Achieving this is a matter of experience and good practice. What we want to do is isolate simple concepts which are obviously correct and then combine them in obviously correct ways. Methodologies include top-down design (break the solution into smaller pieces) and bottom-up design (from smaller pieces build up to the solution) and mixes thereof.
A bottom-up piece that hits me straight away is the task of summing a list of numbers. This has a definition in Haskell's Prelude called sum :: (Num a, Foldable t) => t a -> a. Somewhere in the final solution we are going to use this.
Another method is to simplify the problem. We can be lead astray by the way a problem is phrased. Upon closer inspection we might find an equivalent and simpler phrasing.
What information do we actually need from the input? Just the lists of numbers. What is the simplest way to obtain the lists of numbers? The number of lists seems irrelevant because there is no need to have this information before we start looking at the lists. Drop the first line and we are left with:
5
1 2 3 4 5
2
-100 100
Then, the length of each list is also irrelevant because we do not need that information before summing the list. Therefore lets also drop every other line from this point:
1 2 3 4 5
-100 100
Now we just have the lists of numbers separated by line returns where each number is separated by a space.
At this point we have a clear way to break apart the solution in a top-down manner. First we simplify the input. Secondly we parse the lists of numbers. Thirdly we sum the lists. Fourthly we print the sums. This is therefore the skeleton of our solution:
simplifyInput :: String -> [String]
parseNumberList :: String -> [Integer]
-- Note we can use `sum` from Prelude to sum the number lists.
printSums :: [Integer] -> IO ()
main :: IO ()
main = getContents >>= printSums . fmap (sum . parseNumberList) . simplifyInput
Now it is just a matter of implementing each obvious piece of the solution.
simplifyInput :: String -> [String]
simplifyInput = dropEveryOther . drop 1 . lines
where
dropEveryOther :: [a] -> [a]
In writing simplifyInput I discovered that dropping every other line requires some more work. That is okay, we can just break the solution apart again.
dropEveryOther :: [a] -> [a]
dropEveryOther [] = []
dropEveryOther (x:y:xs) = y : dropEveryOther xs
Then continuing...
parseNumberList :: String -> [Integer]
parseNumberList = fmap read . words
printSums :: [Integer] -> IO ()
printSums = putStr . unlines . fmap show
Therefore, in totality:
simplifyInput :: String -> [String]
simplifyInput = dropEveryOther . drop 1 . lines
where
dropEveryOther :: [a] -> [a]
dropEveryOther [] = []
dropEveryOther (_:y:xs) = y : dropEveryOther xs
parseNumberList :: String -> [Integer]
parseNumberList = fmap read . words
printSums :: [Integer] -> IO ()
printSums = putStr . unlines . fmap show
main :: IO ()
main = getContents >>= printSums . fmap (sum . parseNumberList) . simplifyInput
The amount of code we have has gone up (compared to the first solution) but in exchange the code is made obvious. Now you should add some documentation comments so we do not forget our explanation for the solution.
Alec posted a super compressed version of my original code in one of the comments. I decided to post a small breakdown, in case someone gets lost and has no idea what's going on in there :)
Snippets below need to be preceded with valid imports:
import Control.Monad
import Control.Applicative
So we start with Alec's version:
main = readLn >>= flip replicateM_ (getLine >> sum . map read . words <$> getLine >>= print)
He used the flip function in order to remove one set of parenthesis:
main = readLn >>= (`replicateM_` (getLine >> (print =<< sum . map read . words <$> getLine)))
He used the infix notation for replicateM_ in order to partially apply the second parameter of replicateM_, we can replace is with a lambda:
main = readLn >>= \n -> replicateM_ n (getLine >> (print =<< sum . map read . words <$> getLine))
Now let's start extracting some pieces of code into separate meaningful functions:
printBatchResult = print =<< sum . map read . words <$> getLine
main = readLn >>= \n -> replicateM_ n (getLine >> printBatchResult)
We can flip the print =<< for more readability:
printBatchResult = sum . map read . words <$> getLine >>= print
main = readLn >>= \n -> replicateM_ n (getLine >> printBatchResult)
And so on:
printBatchResult = sum . map read . words <$> getLine >>= print
handleBatch = getLine >> printBatchResult
main = readLn >>= \n -> replicateM_ n handleBatch
And again:
sumLine = sum . map read . words
printBatchResult = sumLine <$> getLine >>= print
handleBatch = getLine >> printBatchResult
main = readLn >>= \n -> replicateM_ n handleBatch
And one more time:
sumLine = sum . map read . words
handleNumbersLine = sumLine <$> getLine
printBatchResult = handleNumbersLine >>= print
handleBatch = getLine >> printBatchResult
main = readLn >>= (\n -> replicateM_ n handleBatch)
And finally the last time:
sumLine = sum . map read . words
handleNumbersLine = sumLine <$> getLine
printBatchResult = handleNumbersLine >>= print
handleBatch = getLine >> printBatchResult
handleAllBatches n = replicateM_ n handleBatch
main = readLn >>= handleAllBatches
We can replace <$> with fmap:
sumLine = sum . map read . words
handleNumbersLine = fmap sumLine getLine
printBatchResult = handleNumbersLine >>= print
handleBatch = getLine >> printBatchResult
handleAllBatches n = replicateM_ n handleBatch
main = readLn >>= handleAllBatches
We can also remove every partial application:
sumLine line = (sum . map read . words) line
handleNumbersLine = fmap sumLine getLine
printBatchResult = handleNumbersLine >>= \sum -> print sum
handleBatch = getLine >> printBatchResult
handleAllBatches n = replicateM_ n handleBatch
main = readLn >>= \numberOfBatches -> handleAllBatches numberOfBatches
And finally, add signatures:
sumLine :: String -> Int
sumLine line = (sum . map read . words) line
handleNumbersLine :: IO Int
handleNumbersLine = fmap sumLine getLine
printBatchResult :: IO ()
printBatchResult = handleNumbersLine >>= \sum -> print sum
handleBatch :: IO ()
handleBatch = getLine >> printBatchResult
handleAllBatches :: Int -> IO ()
handleAllBatches n = replicateM_ n handleBatch
main = readLn >>= \numberOfBatches -> handleAllBatches numberOfBatches
Some final comments:
>>= - the bind function from monad converts one monad to another (or the same) and transforms its value. In main function it takes IO Int, transformation lambda and returns IO () - the result of the transformation, which is empty and prints result in the process.
>> - (used in handleBatch) ignores the left parameter (how many numbers there are in a line is (arguably) unnecessary) and just returns the right parameter - which is a function handling a line with numbers.
I have a file in the form:
3
1 2
3 4
5 7
Where the first line is the number of lines
I know that:
getInt :: IO Int
getInt = readLn
main = do num <- getInt
print (num)
Reads the first line.
Next, I tried:
readInts :: IO [Int]
readInts = fmap (map read.words) getLine
For read a line and get a list: [a,b].
And I tried to use the above in a recursive loop
loop :: Int -> IO()
loop n = if 1 == n then do num <- readInts
print(num)
else loop (n-1)
I'm getting the first line only:
[5,3]
But I need to read the rest of lines, given N
The format of the input file looks a lot like the ones used in programming contests. Here is my standard setup for programming contests like that:
import Control.Monad
import Text.Printf
main :: IO ()
main = do
n <- readLn
forM_ [1 .. n] $ \i -> do
printf "Case %d: " (i :: Int)
solve
An example of solve might be:
solve :: IO ()
solve = do
nums <- map read . words <$> getLine
print (sum nums)
Adding to this helpful answer and comment, for some challenges you'll need to collect n lines and then emit a single result at the end based on an aggregation of the data. An approach for creating a list might use replicateM as follows:
import Control.Monad
toInt :: String -> Int
toInt x = read x :: Int
lineToInts :: String -> [Int]
lineToInts x = map toInt $ words x
main :: IO ()
main = do
n <- readLn
remainingLines <- replicateM n getLine
let list = map lineToInts remainingLines
print list
Sample run:
3
0 1
3 4
6 8
[[0,1],[3,4],[6,8]]
See also Read n lines input with Haskell
Have you looked into the function lines? It takes a string and returns the same string as a list separated by \n. Using this function you don't even have to have the number of lines.
I'm trying to write a program that reads an integer n from the user, then reads n integers (on separate lines), and finally display the sum of the n numbers read.
Here is my code so far:
addNumbers :: IO ()
addNumbers = do
putStrLn "Enter a number:"
num <- getInt
addNumbers2 num
addNumbers2 :: Int -> IO ()
addNumbers2 num = do
putStrLn "Enter a number:"
n <- getInt
if num == 1 then
print n
else do
print (n + addNumbers2 (num - 1))
At the moment it doesn't compile, the error says:
Couldn't match expected type `Int' with actual type `IO ()'
In the return type of a call of `addNumbers2'
In the second argument of `(+)', namely `addNumbers2 (num - 1)'
In the first argument of `print', namely
`(n + addNumbers2 (num - 1))'
IO is really confusing me, I'm trying to get an output of:
Enter a number:
3
Enter a number:
2
Enter a number:
1
Enter a number:
5
Sum is: 8
You treated addNumbers as if it were an ordinary function, but it's an IO operation, so we can only get numbers out of it inside do and with answer <- addNumbers2, but also at the moment it doesn't return anything, it just prints it.
I've refactored a little:
addNumbers :: IO ()
addNumbers = do
putStrLn "Enter how many numbers:" -- clearer
num <- getInt
sum <- addNumbers2 num -- use new version to return sum
print sum -- print them here
and now addNumbers2 actually adds them and returns them:
addNumbers2 :: Int -> IO Int
addNumbers2 num = do
putStrLn "Enter a number:"
n <- getInt
if num == 1 then
return n -- pass the number back
else do
therest <- addNumbers2 (num - 1) -- get the rest of them
return (n + therest) -- add them up
That works:
addNumbers
Enter how many numbers:
3
Enter a number:
1
Enter a number:
2
Enter a number:
3
6
A better way
sequence :: Monad m => [m a] -> m [a] takes a list of actions and runs them all, returning the list of results. If we just make a list full of getInts, [getInt| _<-[1..num]] or more consisely, replicate num getInt we could do numbers <- sequence (replicate num getInt). There's a shorthand for that in Control.Monad, called replicateM :: Monad m => Int -> m a -> m [a]
This would be better done like this though:
import Control.Monad
addNumbers' = do
putStrLn "Enter how many numbers:"
num <- getInt
numbers <- replicateM num (putStrLn "Enter a number" >> getInt)
print (sum numbers)
which gives
Enter how many numbers:
3
Enter a number
10
Enter a number
20
Enter a number
30
60
You can (and should) use combinators available
addNumbers2 n = do
n_numbers <- replicateM n (putStrLn "Number, please: " >> getInt)
let result = sum n_numbers
return result
The crucial insight is to combine the IO actions
putStrLn "string" :: IO ()
getInt :: IO Int
to
(putStrLn "Number?" >> getInt) :: IO Int
So we have a IO action that asks for input and reads it.
Now, we can use
replicateM :: Int -> IO a -> IO [a]
and since we pass an IO action that returns Int, we get a list of Int back.
numbers <- replicateM n (putStrLn "Number?" >> getInt)
runs the given IO action n times and collects their results.
All that is left is summing up the numbers and returning them in the IO Monad.
Or, if you just want to print the sum, you can also
replicateM n (putStrLn "Number?" >> getInt) >>= putStrLn . show . sum
The following pipes-based solution has one tiny advantage over the accepted solution, which is that it will not stack overflow on a large number of lines:
import Pipes
import qualified Pipes.Prelude as Pipes
main = do
numLines <- readLn
total <- Pipes.sum (Pipes.replicateM numLines readLn)
print total
Example use:
$ ./example
3<Enter>
10<Enter>
20<Enter>
30<Enter>
60