Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a getInt function on Haskell?

Tags:

io

haskell

I would like to have a getInt :: IO Int function on Haskell. It would be a function that would take an integer from stdin, leaving the rest of the buffer intact.

The library function with this type I have found, readLn :: IO Int, for example, would not work with an input like:

2
3 4

Because it would take the entire line 3 4, instead of taking 3 and leaving 4 for the next getInt. Although I know I could read the complete string and then split it using words, I would like to ask if there is a way which does not consume the buffer.

Is there any function on the standard libraries doing this? Is there any simple/obvious way to create a getInt I am missing?

like image 494
Mario Román Avatar asked Jul 11 '16 18:07

Mario Román


2 Answers

You could write such a function using hLookAhead and catching isEOFError. However, you'll be mixing your IO with your parsing.

A more Haskellish solution is to define:

parseInts :: String -> [Int]
parseInts str = map read (words str)

and then use parseInts like this:

-- read an entire file and convert to a list of Ints
nums <- fmap parseInts getContents


-- read just a line and convert to a list of Ints
nums <- fmap parseInts getLine

To parse a file which looks like:

3 4
1 1 1 1
2 2 2 2 
3 3 3 3

(e.g. 3 = number of rows, 4 = number of columns), you have a bunch of options:

import Control.Monad (replicateM)

main = do
  (nrows : ncols : _) <- fmap parseInts getLine
  rows <- replicateM nrows $ fmap parseInts getLine

or

import Data.List.Split (chunksOf)

main = do
  (nrows : ncols : rest) <- fmap parseInts getContents
  let rows = chunksOf ncols rest

or even:

main = do
  (nrows : ncols : _) <- fmap parseInts getLine
  forM_ [1..nrows] $ \i -> do
    row <- fmap parseInts getLine
    ... do something with row...
like image 170
ErikR Avatar answered Sep 28 '22 14:09

ErikR


Here I turn my comment into an answer, since it seems the original asker is okay with consuming separators. First we will define a getWord analog to getLine; then we will readIO the result. We will be careful to catch EOF exceptions in case they occur.

import Control.Exception
import Control.Applicative
import Data.Char
import System.IO.Error

getWord :: IO String
getWord = handle handleEOF $ do
  c <- getChar
  if isSpace c then return [] else (c:) <$> getWord
  where
  handleEOF e = if isEOFError e then return [] else throwIO e

readWord :: Read a => IO a
readWord = getWord >>= readIO

Beware: this will of course not work well for values to be read that have a space in them (like Data.Maps or other complex data types).

like image 24
Daniel Wagner Avatar answered Sep 28 '22 15:09

Daniel Wagner