Haskell-循环返回用户输入整数

时间:2018-10-06 21:50:14

标签: haskell recursion io

我想编写一个函数,该函数在被调用时将不停地询问用户输入,直到输入可以被读取为整数为止(此时,该整数将返回到可能的do块中,在该块中调用了该函数第一名)

我的代码在这里:

lp_reqInt = 
  do
    input1 <- getLine
    if ((readMaybe input1 :: Maybe Int) == Nothing)
      then do 
             putStrLn "(integer input required, please try again)"
             lp_reqInt 
      else let output = fromMaybe (-666) (readMaybe input1 :: Maybe Int)
    return output

尝试编译此错误会在最后一行给出parse error (possibly incorrect indentation or mismatched brackets)的可疑简单错误。 (整个文件中没有使用缩进字符)

如何更改代码以具有预期的行为?那有可能吗?

3 个答案:

答案 0 :(得分:3)

另一个答案讨论了什么地方出了问题以及最小的解决方法。除了可以使您继续进行代码工作的最低限度的东西之外,我认为显示惯用的修补程序(也就是使用模式匹配而不是if)可能也很有趣。所以:

lp_reqInt :: IO Int
lp_reqInt = do
  input1 <- getLine
  case readMaybe input1 of
    Nothing -> do
      putStrLn "(integer input required, please try again)"
      lp_reqInt
    Just n -> return n

这不需要使用-666中的奇怪的回退fromMaybe,这很好。使用模式匹配而不是(==)也有一个更微妙的优势:它不需要基础类型具有Eq实例。对于Int,只有一个,因此在此代码中没有优势,但在其他情况下,它可能更重要。我还将类型签名提升到了顶层。 see here,以进一步讨论该习语。

答案 1 :(得分:1)

您似乎对do-符号的工作方式有点误解。

我给你一个“正确的”版本,我们可以解决这个问题:

lp_reqInt = do
    input1 <- getLine
    let maybeInput = readMaybe input1 :: Maybe Int
    if maybeInput == Nothing
      then do putStrLn "(integer input required, please try again)"
              lp_reqInt 
      else return $ (\(Just x) -> x) maybeInput

请注意顶部的let语句。我可以在此处执行let语句,而不是let-in语句,因为它位于do块的顶层。当您编写let output = fromMaybe (...)时,它不在do语句的顶层,而是在if语句的第二部分,因此它将不起作用。

由于这个原因,您遇到了解析错误:GHC期望伴随着in

答案 2 :(得分:0)

以前的答案很好,但我只是想对那些最终不是在搜索OP所要的而是在搜索相关内容的人提供另一种合理的方法来扩展这个主题。

由于该主题提到了用户输入IO)和整数Maybe Int),因此我们最终使用了{{1} }。此类类型最好在Monad变形金刚下表达,即IO (Maybe Int),它们也很好地充当MaybeT IO Int类成员。

对于这些情况,Haskell有出色的解决方案,因此我们可以解决类似的问题;

Alternative

这是无情的:)

import Control.Monad (msum)
import Control.Monad.Trans.Maybe
import Control.Monad.Trans (lift)
import Text.Read (readMaybe)

lp_reqInt :: MaybeT IO Int
lp_reqInt = msum . repeat $ (lift . putStrLn) "Enter an integer.." >>
                            (MaybeT $ readMaybe <$> getLine)