For example, I want to write a program which will take 3 integers as input from command line. The functions I have learned is readLn
to read values from entire line. But readLn
seems to parse the entire line as a single value. How can I get the three values of one line with haskell?
There several ways you could go about this, but I'd prefer the one using reads
. This function has type Read a => ReadS a
, where ReadS a
is just a type alias for String -> [(a, String)]
. It pulls characters from the provided string, stopping when it no longer successfully parses, then returns a list containing a tuple of the parsed value and the remaining string. It uses a list for a more flexible definition, but in practice you'll never see it return a list more than 1 element. An example would be
> reads "1 2 3" :: [(Int, String)]
[(1, " 2 3")]
> reads " 2 3" :: [(Int, String)]
[(2, " 3")]
Using this function, we can parse a line for Int
s and it'll automatically stop when it fails to parse one or the string is empty. We just need a way to chain together multiple calls. Ideally, we'd like a function with the type (s -> [(a, s)]) -> s -> [a]
so that we can re-use it:
chain :: (s -> [(a, s)]) -> s -> [a]
chain f s = case f s of
[] -> []
[(a, newS)] -> a : chain f newS
xs -> map fst xs ++ chain f (last $ map snd xs)
Then you can use it as
> chain reads "1 2 3" :: [Int]
[1, 2, 3]
> chain reads "1 2 3 asdf" :: [Int]
[1, 2, 3]
> chain reads "asdf 1 2 3" :: [Int]
[]
Then you just have to do
read3Ints :: String -> [Int]
read3Ints = take 3 . chain reads
Read a line with getLine
, split it into words
, and read
each:
readInts :: IO [Int]
readInts = fmap (map read.words) getLine
it reads any number of Ints:
ghci> readInts
1 2 3
[1,2,3]
ghci> readInts
1 2 3 4 5 6
[1,2,3,4,5,6]
You could restrict to three:
read3Ints :: IO [Int]
read3Ints = do
ints <- readInts
if length ints == 3 then return ints else do
putStrLn ("sorry, could you give me exactly three integers, "
++ "separated by spaces?")
read3Ints
which looks like this:
ghci> read3Ints
1 2
sorry, could you give me exactly three integers, separated by spaces?
1 23 , 5, 6
sorry, could you give me exactly three integers, separated by spaces?
1,2,3
sorry, could you give me exactly three integers, separated by spaces?
1 3 6
fmap
fmap
works a bit like map
, but you can use it more widely:
ghci> fmap (*10) [1,2,3,4]
[10,20,30,40]
ghci> fmap (*10) (Just 5)
Just 50
ghci> map (fmap (*10)) [Left 'b', Right 4, Left 'c', Right 7]
[Left 'b',Right 40,Left 'c',Right 70]
ghci> fmap words getLine
Hello there me hearties!
["Hello","there","me","hearties!"]
In getInts
, I did fmap (map read.words)
to split the line by spaces, then map read
to turn each one into an Int
. The compiler knows I wanted Int
because of the type signature - I'd get an error if I omitted it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With