[英]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.