6

このコードは、stdinの最初の行から処理する行数を読み取り、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

別の方法として、モナドのマッキングを最小限に抑え、do表記を使用しない回答をお楽しみいただけると思いました。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 に答える