基本上我想找到一种方法,以便用户可以输入测试用例的数量,然后输入他们的测试用例。然后,程序可以运行这些测试用例,并按测试用例出现的顺序打印出结果。
所以基本上我有main读取测试用例的数量并将其输入到将从IO读取多次的函数中。它看起来像这样:
main = getLine >>= \tst -> w (read :: String -> Int) tst [[]]
这是w:w :: Int -> [[Int]]-> IO ()
所以我的计划是读取测试用例的数量并运行一个函数,该函数接收每个测试用例并将结果存储到[[]]变量中。因此列表中的每个列表都是输出。 w将以递归方式运行,直到达到0并在单独的行上打印出每个列表。我想知道是否有更好的方法可以做到这一点,因为我必须将一个空列表传入w,这似乎是无关紧要的。
答案 0 :(得分:3)
正如@bheklilr所提到的,你无法更新像[[]]
这样的值。标准函数方法是通过一组递归调用传递累加器。在以下示例中,acc
函数的loop
参数是此累加器 - 它包含到目前为止收集的所有输出。在循环结束时我们返回它。
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
当你对这种模式更加熟悉时,你会认为它是一个 fold (实际上是我们正在进行IO时的monadic折叠)你可以用{{1}来实现它}。
更新:为了帮助解释fmap的工作原理,这里是不使用foldM
编写的等效表达式:
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
知道该怎么做。
答案 1 :(得分:2)
惯用法是使用replicateM
:
runAllTests :: [[Int]] -> IO ()
runAllTests = {- ... -}
main = do
numTests <- readLn
tests <- replicateM numTests readLn
runAllTests tests
-- or:
-- main = readLn >>= flip replicateM readLn >>= runAllTests