Haskell 如何在IO中正确使用readMaybe函数
大约4个月前,我在Haskell开始编程,现在我必须处理Haskell的IO系统。 我已经做了很多IO操作,也没有遇到任何我自己无法解决的问题,但这次我在谷歌上搜索了将近两个小时,没有得到关于函数readMaybe的一些信息。因此,我有以下问题要解决,我已经尝试了许多不同的方法来解决它,但一直以来,我从我的编译器得到相同的失败消息:Haskell 如何在IO中正确使用readMaybe函数,haskell,Haskell,大约4个月前,我在Haskell开始编程,现在我必须处理Haskell的IO系统。 我已经做了很多IO操作,也没有遇到任何我自己无法解决的问题,但这次我在谷歌上搜索了将近两个小时,没有得到关于函数readMaybe的一些信息。因此,我有以下问题要解决,我已经尝试了许多不同的方法来解决它,但一直以来,我从我的编译器得到相同的失败消息: No instance for (Read a0) arising from a use of `readMaybe' The type variable `a0'
No instance for (Read a0) arising from a use of `readMaybe'
The type variable `a0' is ambiguous
我明白编译器想告诉我什么,但我不知道如何解决这个问题。我已经尝试添加类约束,但没有成功。
这是我的一个非常小而简单的程序,它只计算用户输入的有效数字。当用户输入空行时,程序将终止。
这只是我想在以后的项目中使用的一个辅助功能
countNumbers :: IO Int
countNumbers = do
x <- count 0
return x where
count :: Int -> IO Int
count n = do
line <- getLine
case line of
"" -> do
return n
_ -> case readMaybe line of
Just _ -> do
x <- count (n+1)
return x
Nothing -> do
x <- count n
return x
对我来说非常奇怪的是,我已经编写了这样一个使用readMaybe函数的函数,它工作得非常好。。。
这个程序只是要求用户输入一个数字,只要用户输入一个有效的数字,它就会一直询问
getLineInt :: IO Int
getLineInt = do
putStrLn "Please enter your guess"
line <- getLine
case readMaybe line of
Just x -> do
return x
Nothing -> do
putStrLn "Invalid number entered"
x <- getLineInt
return x
getLineInt::IO Int
getLineInt=do
putStrLn“请输入您的猜测”
行吗
返回x
无事可做
putStrLn“输入的号码无效”
为什么会发生这种情况?
在第二个函数中,很明显,readMaybe line
用作String->Maybe Int
,因为类型推断注意到您使用返回x
,因此x
必须是Int
在第一个函数中,您根本不使用的值,您只想检查读取是否成功。但是,由于没有指定类型(类型推断既不显式也不隐式),因此类型变量不明确:
_ -> case readMaybe line of
有一个简单的解决方法:注释类型:
_ -> case readMaybe line :: Maybe Int of
顺便说一下,这与在没有任何类型上下文的情况下在ghci
中使用read
时遇到的行为完全相同:
> read "1234"
<interactive>:10:1:
No instance for (Read a0) arising from a use of `read'
The type variable `a0' is ambiguous
现在如何计算数字?数字是那些单词,其中readMaybeInt
不会返回Nothing
:
countNumbers :: String -> Int
countNumbers = length . filter isJust . map readMaybeInt . words
现在如何计算标准输入中的数字?我们只需获取输入,直到一行完全为空,在所有这些行上映射countNumbers
,然后映射sum
:
lineNumberCount :: IO Int
lineNumberCount =
getContents >>= return . sum . map countNumbers . takeWhile (/= "") . lines
如果您不习惯bind方法,那么基本上
lineNumberCount :: IO Int
lineNumberCount = do
input <- getContents
return . sum . map countNumbers . takeWhile (/= "") . lines $ input
现在IO monad中只有一个函数,所有函数基本上都是标准函数的应用。请注意,getContents
将关闭标准输入的句柄。如果你想使用,最好使用
input :: String -> IO [String]
input delim = do
ln <- getLine
if ln == delim then return []
else input delim >>= return . (ln:)
这与IO无关,因此您可能不理解编译器试图告诉您的内容。readMaybe
的签名中有一个类型变量a
a
必须有一个Read
实例,但除此之外,它可以是任何东西。编译器告诉您,它无法确定您想要的a
是什么
在getLineInt
中,您没有这个问题,因为您正在返回readMaybe
的结果,并且类型签名说它应该是Int
。在countNumbers
中,您没有使用readMaybe
的结果,因此没有任何东西可以用来确定正确的类型。您可以通过添加显式类型签名来解决此问题(我选择了Int
,因为您显然在计算数字):
最后一句话是关于do
notation的:它只是语法上的糖分,你不必一直使用它。您只需编写returnx
,而不是doreturnx
x <- getLineInt
return x
这让事情更具可读性:
getLineInt :: IO Int
getLineInt = do
putStrLn "Please enter your guess"
line <- getLine
case readMaybe line of
Just x -> return x
Nothing -> putStrLn "Invalid number entered" >> getLineInt
getLineInt::IO Int
getLineInt=do
putStrLn“请输入您的猜测”
行返回x
Nothing->putStrLn“输入的数字无效”>>getLineInt
嘿,感谢您快速准确的回答,也感谢您对do符号用法的提示,我不知道这一点。我认为我必须在每个IO操作之前使用关键字do…不仅do从来都不是必需的,它实际上是一元绑定方法(>>=和>>)的语法糖,因此do可以用于任何一元,而不仅仅是IO。
import Control.Monad (liftM)
import Data.Maybe (isJust)
import Text.Read (readMaybe)
readMaybeInt :: String -> Maybe Int
readMaybeInt = readMaybe
countNumbers :: String -> Int
countNumbers = length . filter isJust . map readMaybeInt . words
lineNumberCount :: IO Int
lineNumberCount =
getContents >>= return . sum . map countNumbers . takeWhile (/= "") . lines
input :: String -> IO [String]
input delim = do
ln <- getLine
if ln == delim then return []
else input delim >>= return . (ln:)
lineNumberCount :: IO Int
lineNumberCount =
input "" >>= return . sum . map countNumbers
_ -> case readMaybe line :: Maybe Int of
x <- getLineInt
return x
getLineInt
getLineInt :: IO Int
getLineInt = do
putStrLn "Please enter your guess"
line <- getLine
case readMaybe line of
Just x -> return x
Nothing -> putStrLn "Invalid number entered" >> getLineInt