哈斯克尔:打印案例编号



我写了一个Haskell代码为:

loop = do
x <- getLine
if x == "0"
then return ()
else do arr <- replicateM (read x :: Int) getLine 
let blocks = map (read :: String -> Int) $ words $ unwords arr
putStr "Case X : output = "; -- <- What should X be?
print $ solve $ blockPair blocks;
loop
main = loop

这将在 0 输入处终止。我还想打印案例编号,例如。Case 1, 2 ...

示例运行:

1
10 20 30
Case 1: Output = ...
1
6 8 10
Case 2: Output = ...
0 

有谁知道这是怎么做到的?另外,如果可能的话,你能建议我一种在最后打印输出行的方法吗?

提前谢谢。

对于问题的第一部分,当前案例编号是您希望在程序执行过程中保持的某些"状态"的示例。 毫无疑问,在其他语言中,您会使用可变变量。

在哈斯克尔,有几种方法可以处理状态。 最简单的方法之一(尽管有时有点丑陋)是将状态作为函数参数显式传递,考虑到您已经构建代码的方式,这将非常有效:

main = loop 1
loop n = do
...
putStr ("Case " ++ show n ++ ": Output = ...")
...
loop (n+1)  -- update "state" for next loop

你问题的第二部分涉及更多。 看起来你想要一个提示而不是一个解决方案。 为了帮助您入门,让我向您展示一个函数的示例,该函数读取行,直到用户输入end然后返回所有行的列表,但不包括end(以及一个main函数,该函数使用大部分纯代码对行执行一些有趣的操作):

readToEnd :: IO [String]
readToEnd = do
line <- getLine
if line == "end"
then return []
else do
rest <- readToEnd
return (line:rest)
main = do
lines <- readToEnd
-- now "pure" code makes complex manipulations easy:
putStr $ unlines $
zipWith (n line -> "Case " ++ show n ++ ": " ++ line)
[1..] lines

编辑:我猜你想要一个更直接的答案而不是提示,所以你调整上述方法来阅读块列表的方式是写这样的东西:

readBlocks :: IO [[Int]]
readBlocks = do
n <- read <$> getLine
if n == 0 then return [] else do
arr <- replicateM n getLine
let block = map read $ words $ unwords arr
blocks <- readBlocks
return (block:blocks)

然后main看起来像这样:

main = do
blocks <- readBlocks
putStr $ unlines $
zipWith (n line -> "Case " ++ show n ++ ": " ++ line)
[1..] (map (show . solve . blockPair) blocks)

这在精神上与 K. A. Buhr 的答案相似(关键步骤仍然是将状态作为参数传递),但以不同的方式考虑以演示一个巧妙的技巧。由于IO动作只是普通的Haskell值,因此您可以使用循环来构建操作,该操作将打印输出而不执行它:

loop :: (Int, IO ()) -> IO ()
loop (nCase, prnAccum) = do
x <- getLine
if x == "0"
then prnAccum
else do inpLines <- replicateM (read x) getLine 
let blocks = map read $ words $ unwords inpLines
prnAccumAndNext = do
prnAccum
putStr $ "Case " ++ show nCase ++ " : output = "
print $ solve $ blockPair blocks
loop (nCase + 1, prnAccumAndNext)
main = loop (1, return ())

关于上述解决方案的一些评论:

  • prnAccum,打印结果的操作,就像nCase一样通过递归循环调用进行线程化(我将它们作为样式问题打包成一对,但如果将它们作为单独的参数传递,它会同样好)。
  • 请注意,更新的操作prnAccumAndNext并不直接位于主do块中;而是在let块中定义。这就解释了为什么它不是在每次迭代时执行,而只是在循环结束时执行最终prnAccum时执行。
  • 正如 luqui 所建议的那样,我已经删除了您用于read的类型注释。replicateM电话会议上的那个当然没有必要,另一个也不像blockPair那样以Int列表作为论据那么长,情况似乎是这样。
  • 吹毛求疵:我删除了分号,因为它们不是必需的。此外,如果arr引用"数组",它不是一个非常合适的名称(因为它是一个列表,而不是一个数组),所以我冒昧地将其更改为更具描述性的名称。(你可以在K. A. Buhr的回答中找到一些有用的技巧和风格调整的其他想法。

最新更新