Setzer22
Setzer22

Reputation: 1689

Getting an integer from the console

Is there a way to read an integer from the console in Haskell? I'm asking for something pretty much like C++'s cin or Java's Scanner.nextInt().

And by that I mean that given this input:

1 2 3
2 3
4 25 12 7
1

I should be able to read them all, not at the same time (maybe reading 4 of them, doing some calculations and then read the rest) ignoring the fact that they are in separate lines.

Upvotes: 0

Views: 492

Answers (3)

limp_chimp
limp_chimp

Reputation: 15233

Using Parsec:

import Text.ParserCombinators.Parsec
import Text.Parsec.Numbers
import Control.Applicative ((*>), (<*))
line = spaces *> many1 (parseFloat <* spaces)
main = putStrLn "Enter numbers:" >> fmap (parse line "") getLine >>= print

Running it:

$ ghc parsenums.hs
$ ./parsenums
Enter numbers:
345 23 654 234
[345.0,23.0,654.0,234.0]

A more "manual" way to do it would be something like:

import Data.Char (isDigit, isSpace)

getInts :: String -> [Int]
getInts s = case span isDigit (dropWhile isSpace s) of
  ("", "") -> []
  ("", s) -> error $ "Invalid input: " ++ s
  (digits, rest) -> (read digits :: Int) : getInts rest

Which might be much clearer to see how it works. In fact, here's one that's completely from the ground up:

getInts :: String -> [Int]
getInts s = case span isDigit (dropWhile isSpace s) of
  ("", "") -> []
  ("", s) -> error $ "Invalid input: " ++ s
  (digits, rest) -> strToInt digits : getInts rest

isDigit :: Char -> Bool
isDigit c = '0' <= c && c <= '9'

isSpace :: Char -> Bool
isSpace c = c `elem` " \t\n\r"

charToInt :: Char -> Int
charToInt c = fromEnum c - 48

strToInt :: String -> Int
strToInt s = go 0 s where
  go n [] = n
  go n (c:rest) = go (n * 10 + charToInt c) rest

Upvotes: 0

Zeta
Zeta

Reputation: 105975

The easiest solution is probably

getAll :: Read a => IO [a] 
getAll = fmap (fmap read . words) getContents

getInts :: IO [Int]
getInts = getAll

which will read all input into a single list.

Upvotes: 4

Bartek Banachewicz
Bartek Banachewicz

Reputation: 39400

When in doubt, use Parsec! (not always, and not really, but who cares)

import Text.ParserCombinators.Parsec
import Text.Parsec.Numbers

value = do 
    spaces
    num <- parseFloat
    return num

line = many value

then "rinse and repeat", with getLine until you EOF.

Note: you can do it without Parsec using read and friends, but this way is more extendable and preferred for more complicated grammars.

Upvotes: 0

Related Questions