我想在Haskell上有一个getInt :: IO Int
函数。这将是一个从 stdin 获取整数的函数,保留缓冲区的其余部分。
我发现的这种类型的库函数,例如readLn :: IO Int
,不能用于输入:
2
3 4
因为它需要整行3 4
,而不是将3
和4
留给下一个getInt
。虽然我知道我可以读取完整的字符串,然后使用words
将其拆分,但我想问一下是否有一种不消耗缓冲区的方法。
标准库是否有任何功能?是否有任何简单/明显的方法来创建我遗失的getInt
?
答案 0 :(得分:3)
在这里,我将我的评论转化为答案,因为看起来原始的提问者可以使用分隔符。首先,我们将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.Map
s或其他复杂数据类型)。
答案 1 :(得分:2)
是否有任何简单/明显的方法可以创建我缺少的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
答案 2 :(得分:0)
您可以使用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...
答案 3 :(得分:0)
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)
在这里,您可以根据需要将可能的食物带出一个合适的地方,或者通过修改将其取走
,勇敢的勇气