使用基于终端的应用程序,该应用程序使用多个putStr
/getLine
来收集用户输入。我想使用以下函数来缩短乏味的putStr
getLine
,方法是向它提供一个问题列表,并在列表中返回答案(递归(。尽管getLine
的终止很好,但我无法避免putStr
方面的non-exhaustive patterns
。这不是重复的。使用replicateM
可以完成多个getLine
,但这是我想要的问题/答案的组合。
(***) = hFlush stdout
questionnaire :: [String] -> IO [String]
questionnaire (x:xs) = do
putStr x
(***)
user <- getLine
case user of
"" -> return []
_ -> do
nextUser <- questionnaire xs
return (user : nextUser)
这里实际上不需要显式递归,因此当参数为空列表时,不可能忘记定义questionaire
。
questionaire :: [String] -> IO [String]
questionaire = traverse getWithPrompt
where getWithPrompt x = do
putStr x
(***)
getLine
示例:
> users <- questionaire ["name? ", "name? ", "name? "]
name? alice
name? bob
name? charlie
> users
["alice","bob","charlie"]
getWithPrompt
具有类型String -> IO String
。traverse
有点像map
,只是map getWithPrompt prompts
会给您留下一个类型为[IO String]
的值,traverse
将IO
操作的列表组合为一个单独的IO
操作,该操作执行每个IO
操作,并将其结果收集到一个列表中。
正如Daniel Wagner所指出的,如果输入空字符串,我忘记了停止。为了处理这个问题,我们可以回到您最初的递归,但要记住定义questionnaire []
的含义。
questionnaire :: [String] -> IO [String]
questionnaire [] = return []
questionnaire (x:xs) = do
putStr x
(***)
user <- getLine
case user of
"" -> return []
_ -> do
nextUser <- questionnaire xs
return (user : nextUser)