Data.Ratio
Data.Ratio
Data.Ratio 모듈은 유리수(rational number)를 나타내는 타입과 그에 대한 연산들을 제공해요.
본문
data Integral a => Ratio a
분자(numerator)와 분모(denominator)가 어떤 Integral 타입인 유리수 타입이에요.
instance Integral a => Enum (Ratio a)
instance Integral a => Eq (Ratio a)
instance Integral a => Fractional (Ratio a)
instance Integral a => Num (Ratio a)
instance Integral a => Ord (Ratio a)
instance (Integral a, Read a) => Read (Ratio a)
instance Integral a => Real (Ratio a)
instance Integral a => RealFrac (Ratio a)
instance Integral a => Show (Ratio a)
type Rational = Ratio Integer
임의 정밀도(arbitrary-precision) 유리수로, 두 Integer 값의 비율로 표현돼요. 유리수는 % 연산자를 사용해 만들 수 있어요.
(%) :: Integral a => a -> a -> Ratio a
두 정수의 비율을 만들어요.
numerator :: Integral a => Ratio a -> a
기약분수(reduced form) 형태의 비율에서 분자를 추출해요. 분자와 분모는 공통 인자가 없고 분모는 양수예요.
denominator :: Integral a => Ratio a -> a
기약분수 형태의 비율에서 분모를 추출해요. 분자와 분모는 공통 인자가 없고 분모는 양수예요.
approxRational :: RealFrac a => a -> a -> Rational
두 실수 분수(fractional) 숫자 x와 epsilon에 적용된 approxRational은 x의 epsilon 안에 있는 가장 단순한(simplest) 유리수를 돌려줘요. 유리수 y는 abs (numerator y) <= abs (numerator y')이고 denominator y <= denominator y'일 때 다른 y'보다 더 단순하다고 해요. 어떤 실수 구간에도 유일한 가장 단순한 유리수가 존재해요. 특히 0/1이 모든 유리수 중 가장 단순하다는 점에 유의하세요.
22.1 명세 (Specification)
module Data.Ratio (
Ratio, Rational, (%), numerator, denominator, approxRational
) where
infixl 7 %
ratPrec = 7 :: Int
data (Integral a) => Ratio a = !a :% !a deriving (Eq)
type Rational = Ratio Integer
(%) :: (Integral a) => a -> a -> Ratio a
numerator, denominator :: (Integral a) => Ratio a -> a
approxRational :: (RealFrac a) => a -> a -> Rational
-- "reduce" is a subsidiary function used only in this module.
-- It normalises a ratio by dividing both numerator
-- and denominator by their greatest common divisor.
--
-- E.g., 12 `reduce` 8 == 3 :% 2
-- 12 `reduce` (-8) == 3 :% (-2)
reduce _ 0 = error "Data.Ratio.% : zero denominator"
reduce x y = (x `quot` d) :% (y `quot` d)
where d = gcd x y
x % y = reduce (x ⋆ signum y) (abs y)
numerator (x :% _) = x
denominator (_ :% y) = y
instance (Integral a) => Ord (Ratio a) where
(x:%y) <= (x':%y') = x ⋆ y' <= x' ⋆ y
(x:%y) < (x':%y') = x ⋆ y' < x' ⋆ y
instance (Integral a) => Num (Ratio a) where
(x:%y) + (x':%y') = reduce (x⋆y' + x'⋆y) (y⋆y')
(x:%y) ⋆ (x':%y') = reduce (x ⋆ x') (y ⋆ y')
negate (x:%y) = (-x) :% y
abs (x:%y) = abs x :% y
signum (x:%y) = signum x :% 1
fromInteger x = fromInteger x :% 1
instance (Integral a) => Real (Ratio a) where
toRational (x:%y) = toInteger x :% toInteger y
instance (Integral a) => Fractional (Ratio a) where
(x:%y) / (x':%y') = (x⋆y') % (y⋆x')
recip (x:%y) = y % x
fromRational (x:%y) = fromInteger x :% fromInteger y
instance (Integral a) => RealFrac (Ratio a) where
properFraction (x:%y) = (fromIntegral q, r:%y)
where (q,r) = quotRem x y
instance (Integral a) => Enum (Ratio a) where
succ x = x+1
pred x = x-1
toEnum = fromIntegral
fromEnum = fromInteger . truncate -- May overflow
enumFrom = numericEnumFrom -- These numericEnumXXX functions
enumFromThen = numericEnumFromThen -- are as defined in Prelude.hs
enumFromTo = numericEnumFromTo -- but not exported from it!
enumFromThenTo = numericEnumFromThenTo
instance (Read a, Integral a) => Read (Ratio a) where
readsPrec p = readParen (p > ratPrec)
(\r -> [(x%y,u) | (x,s) <- readsPrec (ratPrec+1) r,
("%",t) <- lex s,
(y,u) <- readsPrec (ratPrec+1) t ])
instance (Integral a) => Show (Ratio a) where
showsPrec p (x:%y) = showParen (p > ratPrec)
showsPrec (ratPrec+1) x .
showString " % " .
showsPrec (ratPrec+1) y)
approxRational x eps = simplest (x-eps) (x+eps)
where simplest x y | y < x = simplest y x
| x == y = xr
| x > 0 = simplest' n d n' d'
| y < 0 = - simplest' (-n') d' (-n) d
| otherwise = 0 :% 1
where xr@(n:%d) = toRational x
(n':%d') = toRational y
simplest' n d n' d' -- assumes 0 < n%d < n'%d'
| r == 0 = q :% 1
| q /= q' = (q+1) :% 1
| otherwise = (q⋆n''+d'') :% n''
where (q,r) = quotRem n d
(q',r') = quotRem n' d'
(n'':%d'') = simplest' d' r' d r
참고: 원문 명세에는 곱셈 연산자가
⋆(스타) 기호로 나타나 있어요. 위 코드 블록은 원문을 그대로 보존했어요.
더 알아보기 (Learn more)
Rational은 임의 정밀도 유리수이므로 정밀한 수치 연산이 필요할 때 유용해요.%연산자로 유리수를 만들고,numerator·denominator로 분자·분모를 얻는 패턴을 익혀 두면 좋아요.RealFrac·Real등 클래스와의 관계는 관련 클래스 문서를 참고해요.