【问题标题】:Haskell - loop returning user input integerHaskell - 循环返回用户输入整数
【发布时间】:2019-03-12 00:35:36
【问题描述】:

我想写一个函数,当被调用时,它会无情地询问用户输入,直到输入可以被读取为整数,(此时整数被返回到一个可能的 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) 的可疑简单错误。 (整个文件没有使用缩进字符)

我应该如何更改我的代码以获得预期的行为?这可能吗?

【问题讨论】:

  • 您的let 语句需要in 关键字,因为它不在do 块中。
  • @4castle 你会把in关键字放在哪里?
  • in 会出现在 return 之前。但是,case 将是比if 更好的工具,因为您可以对readMaybe input1 的结果进行模式匹配。

标签: haskell recursion io


【解决方案1】:

您似乎对do-notation 的工作方式略有误解。

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

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-statement 而不是let-in-statement,因为它位于do-block 的顶层。当您编写let output = fromMaybe (...) 时,它不在do-block 的顶层,而是在if-statement 的第二部分,因此它不起作用。

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

【讨论】:

    【解决方案2】:

    另一个答案讨论出了什么问题,以及最小的修复。除了可以让您继续编写代码的最简单的事情之外,我认为展示惯用的修复方法可能也很有趣,即使用模式匹配而不是 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
    

    这不需要在fromMaybe 中使用奇怪的后备-666,这很好。使用模式匹配代替(==) 还有一个更微妙的优势:它不需要底层类型具有Eq 实例。对于Int,有一个,所以这段代码没有优势,但在其他情况下它可能更重要。我还将类型签名提升到了顶层; see here 进一步讨论这个成语。

    【讨论】:

      【解决方案3】:

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

      由于该主题提到了 User Input (IO) 和 Integer (Maybe Int),因此我们最终得到了 IO (Maybe Int) 这样的类型。这种类型最好在 Monad Transformers 下表达,即MaybeT IO Int,它们也可以很好地作为Alternative 类成员。

      Haskell 为这些情况提供了出色的解决方案,这样我们就可以解决同样的问题:

      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)
      

      这是无情的:)

      λ> runMaybeT lp_reqInt
      Enter an integer..
      boru
      Enter an integer..
      not an integer
      Enter an integer..
      42
      Just 42
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-16
        • 2020-08-15
        • 1970-01-01
        • 1970-01-01
        • 2021-05-13
        • 1970-01-01
        • 2016-04-25
        相关资源
        最近更新 更多