Haskell如何终止递归调用并避免非穷举模式



使用基于终端的应用程序,该应用程序使用多个putStr/getLine来收集用户输入。我想使用以下函数来缩短乏味的putStrgetLine,方法是向它提供一个问题列表,并在列表中返回答案(递归(。尽管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 Stringtraverse有点像map,只是map getWithPrompt prompts会给您留下一个类型为[IO String]的值,traverseIO操作的列表组合为一个单独的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)

最新更新