【问题标题】:Is there a getInt function on Haskell?Haskell 上有 getInt 函数吗?
【发布时间】:2016-07-11 18:15:24
【问题描述】:

我想在 Haskell 上有一个 getInt :: IO Int 函数。这将是一个从 stdin 中获取整数的函数,而缓冲区的其余部分保持不变。

我发现的这种类型的库函数,例如readLn :: IO Int,不适用于如下输入:

2
3 4

因为它将占用整行 3 4,而不是占用 3 并留下 4 到下一个 getInt。虽然我知道我可以读取完整的字符串,然后使用words 将其拆分,但我想问一下是否有不消耗缓冲区的方法。

标准库中是否有执行此操作的功能?是否有任何简单/明显的方法来创建我失踪的getInt

【问题讨论】:

  • 您的函数使用Ints 之间的分隔符是否正常?如果是这样,似乎很简单,可以创建一个调用 getChar 的循环,直到看到分隔符,然后在生成的 String 上调用 readIO
  • 也许在getLine实现中将'\n'更改为''或类似的东西(?)hackage.haskell.org/package/base-4.9.0.0/docs/src/…
  • 这基本上就是我所描述的,是的,但首先我建议跳过标准 getLine 实现的所有缓冲感知幻想。
  • 哦,太好了,我还没有看到那个评论。我认为它会完美地工作。谢谢,丹尼尔瓦格纳

标签: haskell io


【解决方案1】:

您可以使用hLookAhead 并捕获isEOFError 来编写这样的函数。但是,您会将 IO 与解析混合在一起。

更 Haskellish 的解决方案是定义:

parseInts :: String -> [Int]
parseInts str = map read (words str)

然后像这样使用parseInts

-- read an entire file and convert to a list of Ints
nums <- fmap parseInts getContents


-- read just a line and convert to a list of Ints
nums <- fmap parseInts getLine

解析一个看起来像这样的文件:

3 4
1 1 1 1
2 2 2 2 
3 3 3 3

(例如 3 = 行数,4 = 列数),您有很多选择:

import Control.Monad (replicateM)

main = do
  (nrows : ncols : _) <- fmap parseInts getLine
  rows <- replicateM nrows $ fmap parseInts getLine

import Data.List.Split (chunksOf)

main = do
  (nrows : ncols : rest) <- fmap parseInts getContents
  let rows = chunksOf ncols rest

甚至:

main = do
  (nrows : ncols : _) <- fmap parseInts getLine
  forM_ [1..nrows] $ \i -> do
    row <- fmap parseInts getLine
    ... do something with row...

【讨论】:

    【解决方案2】:

    在这里,我将我的评论变成了答案,因为最初的提问者似乎可以使用分隔符。首先我们将定义一个getWord 模拟到getLine;然后我们将readIO 结果。我们会小心捕捉 EOF 异常,以防它们发生。

    import Control.Exception
    import Control.Applicative
    import Data.Char
    import System.IO.Error
    
    getWord :: IO String
    getWord = handle handleEOF $ do
      c <- getChar
      if isSpace c then return [] else (c:) <$> getWord
      where
      handleEOF e = if isEOFError e then return [] else throwIO e
    
    readWord :: Read a => IO a
    readWord = getWord >>= readIO
    

    注意:这当然不适用于要读取的包含空格的值(例如 Data.Maps 或其他复杂数据类型)。

    【讨论】:

    • 遇到EOF会怎样?为了稳健地做到这一点,我认为你需要使用hLookAhead
    • @ErikR 我已更新getWord 以优雅地处理 EOF。
    • 为什么不使用handleJust :: Exception e =&gt; (e -&gt; Maybe b) -&gt; (b -&gt; IO a) -&gt; IO a -&gt; IO ahandleJust (guard . isEOFError) (\() -&gt; return []) $ do ...
    • @dfeuer 为什么这样更好?当前代码简单、易读且明确。你的版本有什么优势?
    • @DanielWagner,它只是避免明确地重新抛出异常。您也可以使用handle 来避免翻转。
    【解决方案3】:

    是否有任何简单/明显的方法来创建我缺少的 getInt?

    没有。没有一个。您正在寻找沿 scanf("%d") 的东西,它会忽略所有前导空格并且如果没有数字则不会更改缓冲区。在这种情况下,您需要向前看:

    import Data.Char (isSpace, isDigit, digitToInt)
    import Data.List (foldl')
    import Control.Monad (when)
    import System.IO (hGetChar, hLookAhead, Handle, stdin)
    
    getInt :: IO (Maybe Int)
    getInt = hGetInt stdin
    
    hGetIntegral :: (Integral n) => Handle -> IO (Maybe n)
    hGetIntegral h = do 
      hSkipSpace h
      digits <- hGetDigits h
      return $ case digits of
        [] -> Nothing
        xs -> Just $ foldl' (\x a -> 10 * a + x) 0 . map digitToInt $ xs
    
    hGetDigits :: Handle -> IO [Char]
    hGetDigits h = do
      la <- hLookAhead h
      if isDigit la then hGetChar h >> fmap (la:) (hGetDigits h)
                    else return []
    
    hSkipSpace :: Handle -> IO ()
    hSkipSpace h = do
      la <- hLookAhead h
      when (isSpace la) $ hGetChar h >> hSkipSpace h
    

    【讨论】:

    • 顺便说一下,withCString "%d" $ \str -&gt; with 0 $ \i -&gt; c_scanf str i &gt;&gt; peek i 将提供与scanf("%d", %i) 相同的行为。但这只是为了好玩,不应该与其他 Haskell 函数混用。
    • 很好地使用了hLookAhead。也许我们也应该检查isEOF
    【解决方案4】:
    readInt :: [Char] -> Maybe Int
    readInt [] = Nothing
    readInt str = case reads str :: [(Int, String)] of
        [(x ,"")] -> (Just x)
        _ -> Nothing
    
    getInt :: IO (Maybe Int)
    getInt = do
        nums <- getLine
        return (readInt nums)
    
    

    给你,如果需要的话,你可以把它拿出来,一个很好的地方,只是或修改它

    ,勇敢的勇气

    【讨论】:

      猜你喜欢
      • 2014-04-23
      • 2011-11-05
      • 1970-01-01
      • 2011-03-17
      • 2018-04-30
      • 2016-11-27
      • 2013-07-21
      • 1970-01-01
      • 2021-12-20
      相关资源
      最近更新 更多