首页 > 解决方案 > Haskell 如何终止递归调用并避免非穷举模式

问题描述

在基于终端的应用程序上工作,该应用程序使用多个putStr/getLine来收集用户输入。想使用以下函数putStr getLine通过向其提供问题列表并在列表中返回答案(递归)来缩短繁琐。尽管getLines 很好地终止了,但我无法避免事情non-exhaustive patternsputStr一面。这不是重复的。可以使用多个getLine来完成replicateM,但它是我所追求的问题/答案的组合。

(***) = 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)

标签: haskelliopattern-matching

解决方案


这里实际上不需要显式递归,因此不可能忘记定义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,除了 wheremap getWithPrompt prompts会给你一个 type 的值[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)

推荐阅读