簡體   English   中英

Haskell IO的多行讀取技術

[英]Technique for reading in multiple lines for Haskell IO

基本上,我想找到一種方法,以便用戶可以輸入測試用例的數量,然后輸入他們的測試用例。 然后,程序可以運行那些測試用例並按照測試用例出現的順序打印出結果。

因此,基本上我有main來讀取測試用例的數量,並將其輸入到可以從IO讀取多次的函數中。 看起來像這樣:

main = getLine >>= \tst -> w (read :: String -> Int) tst [[]]

這是w的方法簽名: w :: Int -> [[Int]]-> IO ()

因此,我的計划是讀取多個測試用例,並運行一個接受每個測試用例的函數,並將結果存儲到[[]]變量中。 因此,列表中的每個列表都是一個輸出。 w會遞歸運行,直到達到0,然后在單獨的行上打印出每個列表。 我想知道是否有更好的方法,因為我必須將一個空列表傳遞給w,這似乎是多余的。

如@bheklilr所述,您不能更新[[]]類的值。 標准功能方法是通過一組遞歸調用傳遞累加器。 在以下示例中, loop函數的acc參數是該累加器-它包含到目前為止收集的所有輸出。 在循環結束時,我們將其返回。

myTest :: Int -> [String]
myTest n = [ "output line " ++ show k ++ " for n = " ++ show n | k <- [1..n] ]

main = do
  putStr "Enter number of test cases: "
  ntests <- fmap read getLine :: IO Int
  let loop k acc | k > ntests = return $ reverse acc
      loop k acc = do
        -- we're on the kth-iteration
        putStr $ "Enter parameter for test case " ++ show k ++ ": "
        a <- fmap read getLine :: IO Int
        let output = myTest a         -- run the test
        loop (k+1) (output:acc)
  allOutput <- loop 1 []
  print allOutput

隨着您對這種模式的適應程度提高,您將把它識別為折疊 (由於我們正在做IO,所以實際上是一元折疊),您可以使用foldM實現它。

更新 :為了幫助解釋fmap的工作原理,以下是不使用fmap編寫的等效表達式:

With fmap:                                 Without fmap:

n <- fmap read getLine :: IO [Int]         line <- getLine
                                           let n = read line :: Int

vals <- fmap (map read . words) getLine    line <- getLine
             :: IO [Int]                   let vals = (map read . words) line :: [Int]

使用fmap允許我們消除中間變量line ,無論如何我們再也不會引用它。 我們仍然需要提供類型簽名,以便read知道該怎么做。

慣用的方法是使用replicateM

runAllTests :: [[Int]] -> IO ()
runAllTests = {- ... -}

main = do
    numTests <- readLn
    tests <- replicateM numTests readLn
    runAllTests tests
-- or:
-- main = readLn >>= flip replicateM readLn >>= runAllTests

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM