【问题标题】:Writing loops for interactive IO: problems with do-notation and layout为交互式 IO 编写循环:do-notation 和布局的问题
【发布时间】:2011-04-17 18:51:24
【问题描述】:

我刚开始使用 Haskell,它完全让我感到困惑。我之前学过 Java 和 Python,这对我来说更有意义。

我目前正在尝试获取用户输入,检查它是否有效,如果不是:打印错误并再次获取输入;如果有效:从中生成一个布尔值。

更准确地说,我想要一个是/否输入,其中“y”将产生 True,“n”将产生 False,任何其他输入都会在命令行中打印一条消息并要求输入 y/n再次。

例如:

Continue? y/n:
> assd
Invalid input.
Continue? y/n:
> y
(something happens)
Continue? y/n:
> n
(Close program)

用farmiliar格式写,这样的函数:

boolean inputBool() {
    while(True) {
       str = input("Continue? y/n: ");
       if (str == "y") {
           return True;
       } else if (str == "n") {
           return False;
       } else {
           print("Invalid input");
       }
}

--main program--

while(inputBool()) {
    doSomething();
}

自从我刚开始使用haskell(实际上是今天)以来,我不太清楚我能做什么或不能做什么。 我在想类似的东西:

yesno :: Bool
yesno = do
        putStr "Continue? y/n: " 
        str <- readLn
        if (str == "y") then True else (
            if (str == "n") then False else (
                do
                putStrLn "Invalid input."
                yesno
            )    
        )

由于许多原因,这不起作用。我的主要问题是我不知道这个“做什么”是做什么的。我刚刚读到它在需要 io 操作时使用并以某种方式执行以下表达式。在haskell中阅读完所有内容都会评估为一个值后,这对我来说没有意义。 “做什么”评价什么? 另外,预计会有什么缩进?这似乎是一种随机。 我知道该函数必须评估为我的布尔值,使用此“do”操作似乎不可能。但是,如何将某些内容打印到控制台并仍然使其成为计算结果为 True 或 False 的表达式的一部分?

感谢您的帮助。

(顺便说一句。互联网上有活跃的haskell论坛吗?我找不到任何=/)

【问题讨论】:

  • 也许从一个好的教程开始:learnyouahaskell.combook.realworldhaskell.org
  • Haskell 讨论的最佳论坛是 Haskell Reddit,Haskell Stackoverflow 标签; #haskell IRC 频道;或 haskell-cafe@ 邮件列表。
  • 实际上,我真的厌倦了教程和对所有内容的冗长解释。自从我开始编程以来,我所需要的只是语法、关键字、良好的文档和一些练习。有了这个,Java 在几天内就学会了,python 很容易,因为我已经知道了 java。
  • 语法和关键字不会帮助你,如果语言的 语义 与你已经知道的不同——就像 Haskell 一样。你需要一个很好的教程。 learnyouahaskell.com/input-and-output#hello-world
  • 其实我会推荐haskell-beginners@haskell.org而不是上面Don提到的haskell-cafe@haskell.org。

标签: loops haskell io


【解决方案1】:

由于你的函数需要进行IO,所以它必须在IO monad中返回一个值,所以它的类型需要是yesno :: IO Bool,并且需要将TrueFalse的值提升到monad中通过return。您还需要getLine 而不是readLn,因为您需要原始字符串,而不是解析值(这可能会导致解析错误)。最后,您需要在调用getLine 之前先hFlush stdout,否则提示符仍将坐在缓冲区中等待换行符(这需要您导入System.IO)。

我已经把提示字符串变成了一个参数,所以你可以使用这个函数来询问各种是/否问题:

import System.IO

yesno :: String -> IO Bool
yesno prompt = do
          putStr $ prompt ++ " y/n: "
          hFlush stdout
          str <- getLine
          case str of
            "y" -> return True
            "n" -> return False
            _   -> do
              putStrLn "Invalid input."
              yesno prompt

然后,您可以从交互循环中调用您的函数:

main :: IO ()
main = do
         fun <- yesno "Is Haskell fun?"
         if fun
          then putStrLn "rock on"
          else putStrLn "read more tutorials"
         continue <- yesno "Continue?"
         if continue then main else return ()

【讨论】:

  • 非常感谢。我想我明白这一点 - 所以“do”的计算结果是它的最后一个表达式?
  • 基本上。 “do” 块将评估为由 return 生成的值——取决于执行路径。
  • 是的,do 是“语法糖”,它将作为语句的一系列表达式粘合成一个大表达式。胶水运算符是 (>>=) 又名 bind
  • @Maro 假设在它到达那里之前没有发生错误或失败或其他“提前退出”,是的。
  • 你必须小心return。它与 Java 或 Python 中的 return 不同。在 Haskell 中,return 将一个值包装在一个单子外壳中,产生一个单子值。它不会导致do 块停止执行。通常,您会在 do 块中看到 return 作为叶表达式,因此它将是从块返回的值。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-12
  • 1970-01-01
相关资源
最近更新 更多