6

此代码从标准输入的第一行读取要处理的行数,然后循环 number_of_lines_to_process 次进行一些计算并打印结果。我希望它在“#”之后打印“Line #”中的行号,但我不知道如何获取它

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 ()

我想这个问题的解决方案真的很简单,但我不熟悉 Haskell(第一次在里面编码),我没有找到任何方法。任何人都可以帮忙吗?

4

5 回答 5

13

您可以使用forM_而不是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)

请注意,因为您使用forM_(丢弃每次迭代的结果),所以最后不需要附加return ()-do块返回最后一条语句的值,在这种情况下,()它是由forM_.

于 2012-04-30T11:00:22.287 回答
9

诀窍是首先创建一个包含所有要打印的行号的列表,然后遍历该列表,依次打印每个数字。所以,像这样:

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 ()

阅读 的定义forM_

顺便说一句,我不推荐使用旧的 Haskell98 IO 库。改为使用System.IO

于 2012-04-30T10:57:15.267 回答
5

您可以计算结果,枚举它们,然后打印它们:

import IO
import Control.Monad (replicateM)

-- I'm assuming you start counting from zero
enumerate xs = zip [0..] xs

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
        return result
    mapM_ putStrLn [ "Line "++show i++": "++show l | (i,l) <- enumerate lines ]
于 2012-04-30T10:59:24.433 回答
2

我还是 Haskell 的新手,所以下面的程序可能存在问题(它确实有效)。该程序是尾递归实现。doLine辅助函数携带行号。处理步骤被考虑在内process,您可以根据遇到的问题进行更改。

import System.IO
import Text.Printf

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

-- This reads "max" lines from stdin, processing each line and
-- printing the result.
processLines :: Integer -> IO ()
processLines max = doLine 0
    where doLine i
              | i == max = return ()
              | otherwise =
                  do
                    line <- getLine
                    let result = process line
                    Text.Printf.printf "Line #%d: %d\n" (i + 1) result
                    doLine (i + 1)


-- Just an example. (This doubles the input.)
process :: [Char] -> Integer
process line = let number = read line :: Integer
               in
                 number * 2

我是一名haskell 菜鸟,因此欢迎对上述内容提出任何批评。

于 2012-04-30T15:30:23.263 回答
0

作为替代方案,我认为您可能会喜欢一个带有最少的单子弄脏和没有符号的答案。我们使用 enumerate 函数将用户数据的惰性列表与行号的无限列表压缩在一起,以提供我们想要的输出。

import System.IO
import Control.Monad (liftM)

--Here's the function that does what you really want with the data
example = (* 2)

--Enumerate takes a function, a line number, and a line of input and returns
--an ennumerated line number of the function performed on the data
enumerate :: (Show a, Show b, Read a) => (a->b) -> Integer -> String -> String
enumerate f i x = "Line #" ++ 
                  show i ++ 
                  ": " ++ 
                  (show . f . read $ x) -- show . f . read handles our string conversion

-- Runover takes a list of lines and runs 
-- an enumerated version of the sample over those lines.
-- The first line is the number of lines to process.
runOver :: [String] -> [String]
runOver (line:lines) = take (read line) $ --We only want to process the number of lines given in the first line
                       zipWith (enumerate example) [1..] lines -- run the enumerated example 
                                                               -- over the list of numbers and the list of lines

-- In our main, we'll use liftM to lift our functions into the IO Monad
main = liftM (runOver . lines) getContents
于 2012-05-03T21:37:50.260 回答