我想从字符串中读取比率,但我不希望我的程序在分母为零时崩溃。如何检测零分母并避免错误?只是使用readMaybe不起作用:
Prelude Text.Read> readMaybe "1 % 0" :: Maybe Rational
Just *** Exception: Ratio has zero denominator
Run Code Online (Sandbox Code Playgroud)
我创造了这个远非完美的解决方案:
readMaybeRational :: String -> Maybe Rational
readMaybeRational s =
case ((readMaybe $ drop 1 $ dropWhile (/='%') s) :: Maybe Int)
of Just 0 -> Nothing
_ -> readMaybe s
Run Code Online (Sandbox Code Playgroud)
但我不知道如何很好地处理嵌套的比率:
"Just (1 % 0)"
Run Code Online (Sandbox Code Playgroud)
如果我可以覆盖 Ratio 的 Read 实例,当分母为零时,我可以让 readMaybe 返回 Nothing :
instance (Integral a, Read a) => Read (Ratio a) where
readPrec =
parens
( prec ratioPrec
( do x <- step readPrec
expectP (L.Symbol "%")
y <- step readPrec
-- is y 0? If so, do something here
return (x % y)
)
)
Run Code Online (Sandbox Code Playgroud)
但我很确定我不能那样做。
我认为最好的解决方案是newtype包装Ratio,如下所示:
import Control.Monad
import GHC.Read
import GHC.Real
import qualified Text.Read.Lex as L
import Text.ParserCombinators.ReadPrec
newtype SaneReadRatio a = SaneReadRatio (Ratio a)
type SaneReadRational = SaneReadRatio Integer
instance (Integral a, Read a) => Read (SaneReadRatio a) where
readPrec =
parens
( prec ratioPrec
( do x <- step readPrec
expectP (L.Symbol "%")
y <- step readPrec
guard (y /= 0)
return (SaneReadRatio (x % y))
)
)
readListPrec = readListPrecDefault
readList = readListDefault
Run Code Online (Sandbox Code Playgroud)
通过使用SaneReadRationalin 代替 of读取数据Rational,然后在结果上使用coercefrom来使用它Data.Coerce,这会将其更改回底层,Rational无论它在您的类型中埋藏得有多深。