读取比率时如何检测零分母?

bwr*_*oga 11 haskell

我想从字符串中读取比率,但我不希望我的程序在分母为零时崩溃。如何检测零分母并避免错误?只是使用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)

但我很确定我不能那样做。

Jos*_*ica 4

我认为最好的解决方案是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无论它在您的类型中埋藏得有多深。