Haskell ReplicateM IO - haskell

I'm trying to create a function which allows the user to input a list of strings. The function takes the length and allows the user to input length-1 more lines. Then each line is checked to ensure it is the same length as the original line. However, I'm having a few problems and that I can't find a solution to.
The problems are that I can input more than count-1 lines and the length isn't being calculated as I expected.. for example if I input ["12","13"] and then ["121","13"] the error is given, although they are the same length!
read :: IO [Line]
read = do
line <- getLine
let count = length line
lines <- replicateM (count-1) $ do
line <- getLine
if length line /= count
then fail "too long or too short"
else return line
return $ line : lines
Line is of type String.
readLn gives a parse error.

It sounds to me like you're getting confused about the difference between getting a line as a String and reading/parsing a line of input as a custom type. You are using getLine, which always returns exactly the String that the user types. Compare:
Prelude> fmap length getLine
["12","13"]
11
Prelude> length "[\"12\",\"13\"]" -- explanation of the 11
11
Prelude> fmap length (readLn :: IO [String])
["12","13"]
2
Prelude> length ["12", "13"] -- explanation of the 2
2
As demonstrated here, you probably want to use readLn, which first gets a line of input and then parses it with read.
-- defined in the Prelude
readLn = do
s <- getLine
return (read s)
If I modify your code to include the imports and definitions below:
import Control.Monad
type Line = [String]
...and to call readLn instead of getLine, then I can type the literal lines ["12","13"] and ["121","13"] without errors.

Related

Is getLine lazy?

Is getLine lazy?
Say I have a very long line on the input. It's just a sequence of numbers. I only need to sum 3 first numbers. Will getLine be efficient and read only the first part of the line, or do I have to create my own function for lazy line reading, that would read characters one by one?
Will my implementation be efficient if I were to sum the whole line? (Will there be an overhead due to reading characters one by one?)
import Control.Applicative
main = do
line <- getLine'
print $ sum $ map read $ take 3 $ words line
getLine' :: IO String
getLine' = do
c <- getChar
if c == '\n' then return [] else (c:) <$> getLine'
While getLine isn't lazy, getContents is, and it can be combined with functions like lines and words. Therefore, the following program will only read enough of stdin to get (up to) three integers from the first line and print their sum:
main :: IO ()
main = do contents <- getContents
let lns = lines contents
result = sum $ map read $ take 3 $ words $ head lns
print (result :: Integer)
Note that, if you modify the program to access subsequent lines -- for example, if you added:
putStrLn $ take 80 $ lns !! 1
to the bottom of the program to print the first 80 characters of the second line, then the program would have to finish reading the first line (and so would hang for a bit between the last two lines of the program) before processing the first 80 characters of the second. In other words, this lazy line reading is only useful if you only need to read the first bit of the first line, if that wasn't obvious to you -- Haskell doesn't have any magic way to skip the rest of the first line to get to the second.
Finally, note that, for the above program, if there are fewer than three integers on the first line, it'll just sum those numbers and won't try to read past the first line (which I think is what you wanted). If you don't actually care about the line endings and just want to sum the first three numbers in the file, regardless of how they're divided up into lines, then you can break the contents up directly into words like so:
main = do contents <- getContents
let result = sum $ map read $ take 3 $ words contents
print (result :: Integer)

Reading 2-line input Haskell

I've finished writing a Haskell program but I have troubles with the I/O and Main parts of it. I want to read input of the following format:
10 3
1 4 8
The first and second numbers are Ints, and the numbers of the second line should be made into an integer list, [Int]. The length of the list is equal to the second number on the first line.
I have the following code to read one Int at a time, however, it can only get it to work for the first line of input.
getInt :: IO Int
getInt = do
l <- getLine
return (read l)
What am I doing wrong?
you should be able to get the data shown here by:
readData :: IO (Int, [Int])
readData = do
[nr, _] <- map read . words <$> getLine
nrs <- map read . words <$> getLine
return (nr,nrs)
this will read both lines and return the 10 as the first component and [1,4,8] as the second component of a tuple if you read in your example
As the second number in the first line will just be the length of the returned list I skipped it here.
of course you probably will want to adapt this to your needs.

How to do multiple actions in Haskell

Im trying to write a simple function to learn the IO monad in Haskell. The function is supposed to take the sum of some given integers from the console but when the function has run for example 4 times it says "1*** Exception: Char.digitToInt: not a digit '\n'"
import Data.Char
readInts :: IO ()
readInts = do
putStrLn "Choose nr of ints to sum."
c <- getChar
let i = digitToInt c
-- code.
let sum = getInts i
let str = "\nSum is: " ++ [intToDigit i].
putStrLn str
getInt :: IO Int
getInt = do
c <- getChar
return (digitToInt c)
getInts :: Int -> IO Int
getInts n = if n == 0
then return 0
else
do i <- getInt
j <- getInts
return (i+j)
Can somebody please explain where my recursion is going wrong?
You're simply using the wrong tools to convert between "keyboard data" and numbers. Has little to do with IO.
intTodigit, as the name says, acts on single digits / characters, not on general numbers. What you want is to read / print entire strings, which can handle multi-digit numbers. Replace getChar with getLine, digitToInt with read, and [intToDigit i] with show i. Then it should work fine.
However, it would be better to make some more simplifications.
getInt basically exists already, though in more general form: readLn gets a line from stdin and inteprets it as the required type.
getInts is implemented more complicately than necessary. Explicit recursion over a counting variable (BTW, it has to be getInts (n-1) in the recursion) is ugly; such looping is obviously so common that there exists a standard solution (you need to import Control.Monad) for it which looks alot like loops in imperative languages you might be used to:
getIntsAndSum :: Int -> IO Int
getIntsAndSum n = fmap sum . forM [1 .. n] $ \k -> do
i <- getInt
return i
which can in fact be further simplified to
fmap sum . forM [1 .. n] $ \_ -> getInt
because do blocks a just an optional construct to help you chain actions, but when there's only one action you can simply write that on its own.
Ideally, you would not have to first ask for the number of numbers at all: just gather all numbers you're given, and sum them up. This works, as jamshidh said, very simply with interact.
main = interact processInput
where processInput allInput = show (sum allNumbers)
where allNumbers = map read . lines allInput
and that's it! Nothing else needed. In fact, this can be written yet simpler: you basically have just one simple data-flow pipeline.
main = interact $ show . sum . map read . lines
getChar gets every char, including any "\n" that you type in (which you need to submit the answer). Try filtering these out.
A better way to solve this could be to use "interact" to get the user input, and break apart the data using lines. That way the user could input multi digit numbers, and the "\n"'s would be removed.
The following sums numbers entered at the command line (without any prompting, end inputting by typing ^d).
main = interact (show . sum . fmap read . lines)

Reading from file which contain dots

I have file txt which contain two numbers, for example:
2.
3.
How can you see, each end of line is ended with dot.
How can I read it and print for example sum of this numbers?
If my file don't contain dots, so for example I have
2
3
it isn't problem. My code is:
main3 = do
x <- openFile "C:/Users/file.txt" ReadMode
m <- hGetLine x
n <- hGetLine x
return ((read m::Int)+(read n::Int))
and it work good. But when in my file are dots, i don't know what can I do. Maybe are any library?
Thank's for help.
The trouble is that read won't parse "3." to mean 3. If you just want to do this in a hacky way, you can drop the last character.
main4 = do
x <- openFile "C:/Users/file.txt" ReadMode
m <- hGetLine x
n <- hGetLine x
return ((read (init m)::Int)+(read (init n)::Int))
which may work but is quite fragile since it assumes that the only non-digit character in each line is the last. We can do a little better by assuming the first n digit characters form our numbers
import Data.Char
-- takeWhile isDigit :: String -> String
main5 path = do
f <- readFile path
numberStrings <- map (takeWhile isDigit) (lines f)
sum (map read numberStrings)
The most robust solution would be to upgrade to a "parser combinator library" like Parsec which would let you write out the grammar of your text file.
There are a few ways to do it.. The simplest being
main = do
text <- readFile "file.txt" -- Grab the file
let nums = map read . map init . lines $ text
print $ sum nums
init just drops the .. However I'd write it like this
import Text.Parsec.String
import Text.Parsec
import Control.Applicative ((<*), (<$>))
getNums :: Parser [Int]
getNums = num `sepEndBy` newline
where num = read <$> many1 digit <* char '.'
main = parseFromFile getNums "filename" >>= print . fmap sum
Is it worth using parsec for something like this? The answer is "it depends". My rule of thumb is that if I'm planning to use it more than once, then just bite the bullet and use parsec. It's much, much easier to modify something that uses a library like parsec for new and more complex formats. Plus you get free [decent] error messages this way.
You can to this in several steps, exploiting Haskell's monadic API:
Read in the file line per line
Check whether each line is ended with a dot: Use map and return a Maybe monad: Nothing when the line is not ended with a dot, Just (value without dot) when the line ended with a dot
Use map + flatten to convert each String to a number, again with the Maybe monad. Nothing when there is an error, or Just x to contain the number
Sum the values using fold
When I have some time, I will try to build an example. It's always fun to play with Haskell's monadic API.

Haskell IO reverse string input

I keep getting an error: couldn't match expected type 'Bool' with actual type '[t0]'. I'm trying to get user inputs of string and then output however many strings in reversed ORDER.
Example input:
HI1
HI2
Example output:
HI2
HI1
My code:
Back :: Int -> IO()
Back x = do line <- sequence_[getLine|[1..x]]
mapM_ print (reverse line)
To expand on Vitus's comment, import Control.Monad, then
back count = do
lines <- replicateM count getLine
mapM_ putStrLn (reverse lines)
If this doesn't work for you, please say what error message you get, or give an example of incorrect output.
In this case, we can forego do notation fairly easily:
back count = mapM_ putStrLn . reverse =<< replicateM count getLine
Or
back count = mapM_ putStrLn =<< liftM reverse (replicateM count getLine)
You may or may not find either of those to be clearer.
Note that your function name must start with a lower case letter, e.g. back. Back as a function name is a syntax error.
Also note that indentation is significant. The indentation of the do block in your question is wrong; the do blocks in my and melpomene's answers are correctly indented.
back :: Int -> IO ()
back x = do line <- sequence [getLine | _ <- [1 .. x]]
mapM_ putStrLn (reverse line)

Resources