Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do something with data from stdin, line by line, a maximum number of times and printing the number of line in Haskell

Tags:

haskell

monads

This code reads the number of lines to process from the first line of stdin, then it loops number_of_lines_to_process times doing some calculations and prints the result. I want it to print the line number in "Line #" after "#" but I don't know how to obtain it

import IO
import Control.Monad (replicateM)

main :: IO ()

main = do
    hSetBuffering stdin LineBuffering
    s <- getLine
    let number_of_lines_to_process = read s :: Integer
    lines <- replicateM (fromIntegral(number_of_lines_to_process)) $ do
        line <- getLine
        let number = read line :: Integer
            result = number*2 --example
        putStrLn ("Line #"++": "++(show result)) --I want to print the number of the iteration and the result
    return ()

I guess that the solution to this problem is really easy, but I'm not familiar with Haskell (coding in it for the first time) and I didn't find any way of doing this. Can anyone help?

like image 246
ICTylor Avatar asked Apr 30 '12 10:04

ICTylor


2 Answers

You could use forM_ instead of replicateM:

import IO
import Control.Monad

main :: IO ()
main = do
    hSetBuffering stdin LineBuffering
    s <- getLine
    let number_of_lines_to_process = read s :: Integer

    forM_ [1..number_of_lines_to_process] (\i -> do
        line <- getLine
        let number = read line :: Integer
            result = number * 2
        putStrLn $ "Line #" ++ show i ++ ": " ++ show result)

Note that because you use forM_ (which discards the results of each iteration) you don't need the additional return () at the end - the do block returns the value of the last statement, which in this case is the () which is returned by forM_.

like image 179
Chris Taylor Avatar answered Oct 20 '22 20:10

Chris Taylor


The trick is to first create a list of all the line numbers you want to print, and to then loop through that list, printing each number in turn. So, like this:

import Control.Monad

import System.IO

main :: IO ()
main = do
  hSetBuffering stdin LineBuffering
  s <- getLine
  let lineCount = read s :: Int
      -- Create a list of the line numbers
      lineNumbers = [1..lineCount]

  -- `forM_` is like a "for-loop"; it takes each element in a list and performs
  -- an action function that takes the element as a parameter
  forM_ lineNumbers $ \ lineNumber -> do
    line <- getLine
    let number = read line :: Integer
        result = number*2 --example
    putStrLn $ "Line #" ++ show lineNumber ++ ": " ++ show result
  return ()

Read the definition of forM_.

By the way, I wouldn't recommend using the old Haskell98 IO library. Use System.IO instead.

like image 36
dflemstr Avatar answered Oct 20 '22 21:10

dflemstr