【问题标题】:How to readline in Haskell without transfer to the next line?如何在 Haskell 中读取行而不转移到下一行?
【发布时间】:2016-11-21 09:41:12
【问题描述】:

我正在尝试在 Haskell 中使用 readline 而不将光标转移到下一行。

比如我写了如下代码:

readEvalPrintLoop :: IO()
readEvalPrintLoop = do
    line <- getLine
    case line of
            "bye" -> return ()
            line   -> do putStrLn $ interpret line
                         readEvalPrintLoop

启动程序后:

> 1 2 + . <enter>
3

但我希望找到最简单的方法来获得关注

> 1 2 + . <enter> 3

【问题讨论】:

  • @Bakuriu 是的,它应该是您提到的输入缓冲。请注意,此缓冲区不存在于 Haskell 应用程序中,而是存在于终端中(在 OS 内核或终端仿真器中)。默认情况下,终端会像往常一样回显每个键并移动光标,而无需与应用程序进行任何交互。应用程序可以要求终端更改此默认行为,实际上 ncurses 是一种常见的方法。
  • @Bakuriu 缓冲只是切线相关:这里真正的问题是回显。在我的回答中,我关闭了缓冲,但不是为了防止换行符出现;相反,它是允许其他所有内容一次出现而不是缓冲区大小的块!
  • @Andrei 如果你和我一样,这个问题可能会让你想知道到底发生了什么以及哪些行为是可配置的。您可以在The TTY demystified 阅读更多相关信息,但我警告您:如果您只想完成工作,这将比您想要的信息多得多!

标签: haskell io console readline


【解决方案1】:

当您按下回车键时——就像按下任何其他键时一样——它会打印到屏幕上。您可以使用hSetEcho 关闭此行为:

main = do
    hSetEcho stdin False
    readEvalPrintLoop

但是,如果您这样做,您会发现这有一些附带损害:输入不再回显,但您键入的任何其他内容也没有,因此看起来您输入的所有内容都是不可见的!您可以通过手动实施修改后的回显策略来解决此问题。您需要做两件事:使用hSetBuffering 将输入流设置为无缓冲模式(以便您立即接收输入,而不是在行尾)并打印您收到的每个非输入字符。所以:

myGetLine :: IO String
myGetLine = do
    c <- getChar
    case c of
        '\n' -> return "" -- don't echo newlines
        _ -> do
            putChar c -- do echo everything else
            fmap (c:) myGetLine

readEvalPrintLoop :: IO()
readEvalPrintLoop = do
    line <- myGetLine -- getLine changed to myGetLine
    case line of
            "bye" -> return ()
            line   -> do putStrLn $ interpret line
                         readEvalPrintLoop

main = do
    hSetEcho stdin False
    hSetBuffering stdin NoBuffering
    readEvalPrintLoop

【讨论】:

  • 注:根据您想要的行为,您可能需要检查 myGetLine 中的 EOF。
猜你喜欢
  • 2013-11-14
  • 2020-11-28
  • 2015-07-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多