28

我是 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

它运行正常并按预期以大写形式打印参数 - 那么第一个函数有什么问题?

4

2 回答 2

51

给定的 String -> String参数interact应该接受一个包含所有输入的字符串并返回一个包含所有输出的字符串。按下回车后看到输出的原因interact (map toUpper)是因为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)
于 2013-05-28T19:28:00.287 回答
3

更可重用的解决方案:

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 ++ ">"
于 2019-09-15T06:51:30.943 回答