我是Haskell的新手,有interact
函数的问题。这是我的示例程序:
main :: IO ()
main = interact inputLength
inputLength :: String -> String
inputLength input = show $ length input
它编译,但是运行时不打印输出-只打印传递给它的字符串并移动到下一行。当我传递interact
时,另一个String -> String
函数如下所示:
upperCase :: String -> String
upperCase input = map toUpper input
可以正常运行,并按预期打印大写参数——那么第一个函数有什么问题呢?
给interact
的String -> String
参数应该接受包含所有输入的字符串,并返回包含所有输出的字符串。您在使用interact (map toUpper)
按下enter键后看到输出的原因是map toUpper
的行为是惰性的——它可以在所有输入都已知之前就开始输出。查找字符串的长度不是这样的——在产生任何输出之前必须知道整个字符串。
您需要发出EOF信号来表示您已经完成输入(在控制台中,这是Unix/Mac系统上的Control-D,我相信它是Windows上的Control-Z),然后它会给您长度。或者,您可以这样来查找每行的长度:
interact (unlines . map inputLength . lines)
这将在每行中始终是惰性的,因此您知道在每个输入之后可以得到一个输出。
由于行操作是一种常见的模式,所以我喜欢定义一个小的辅助函数:
eachLine :: (String -> String) -> (String -> String)
eachLine f = unlines . map f . lines
那么你可以这样做:
main = interact (eachLine inputLength)
更可重用的解决方案:
main = interactLineByLine processLine
-- this wrapper does the boring thing of mapping, unlining etc.... you have to do all the times for user interaction
interactLineByLine:: (String -> String) -> IO ()
interactLineByLine f = interact (unlines . (map processLine) . lines)
-- this function does the actual work line by line, i.e. what is
-- really desired most of the times
processLine:: String -> String
processLine line = "<" ++ line ++ ">"