如何将Rational转换为“漂亮”的字符串?

时间:2015-06-19 06:29:49

标签: haskell formatting rational-number

我想在其十进制扩展中显示一些Rational值。也就是说,我宁愿显示3 % 4,而不是显示0.75。我希望这个函数是Int -> Rational -> String类型。第一个Int用于指定最大小数位数,因为Rational扩展可能是非终止的。

Hooglehaddocks for Data.Ratio对我没有帮助。我在哪里可以找到这个功能?

5 个答案:

答案 0 :(得分:9)

你可以做到。不优雅,但做的工作:

import Numeric
import Data.Ratio

display :: Int -> Rational -> String
display n x = (showFFloat (Just n) $ fromRat x) ""

答案 1 :(得分:7)

这是一个不使用浮点数的任意精度解决方案:

import Data.Ratio

display :: Int -> Rational -> String
display len rat = (if num < 0 then "-" else "") ++ (shows d ("." ++ take len (go next)))
    where
        (d, next) = abs num `quotRem` den
        num = numerator rat
        den = denominator rat

        go 0 = ""
        go x = let (d, next) = (10 * x) `quotRem` den
               in shows d (go next)

答案 2 :(得分:6)

重复使用library code的任意精确版本:

import Data.Number.CReal

display :: Int -> Rational -> String
display digits num = showCReal digits (fromRational num)

我知道我之前已经看过一个函数,它将理性转化为数字的方式更容易检查(即,这使得数字开始重复的位置非常清楚),但我可以&#39;好像现在找到它。无论如何,如果事实证明是需要的话,写起来并不难;你只需编写通常的长除法算法,并注意你已经完成的划分。

答案 3 :(得分:2)

import Data.List as L
import Data.Ratio

display :: (Integral i, Show i) => Int -> Ratio i -> String
display len rat = (if num < 0 then "-" else "") ++ show ip ++ "." ++ L.take len (go (abs num - ip * den))
  where
    num = numerator rat
    den = denominator rat
    ip  = abs num `quot` den

    go 0 = ""
    go x = shows d (go next)
      where
        (d, next) = (10 * x) `quotRem` den

答案 4 :(得分:1)

这是我几周前写的一篇。您可以指定所需的小数位数(正确舍入),或只传递Nothing,在这种情况下,它将打印完整的精度,包括标记重复的小数。

module ShowRational where
import Data.List(findIndex, splitAt)

-- | Convert a 'Rational' to a 'String' using the given number of decimals.
-- If the number of decimals is not given the full precision is showed using (DDD) for repeating digits.
-- E.g., 13.7/3 is shown as \"4.5(6)\".
showRational :: Maybe Int -> Rational -> String
showRational (Just n) r =
    let d = round (abs r * 10^n)
        s = show (d :: Integer)
        s' = replicate (n - length s + 1) '0' ++ s
        (h, f) = splitAt (length s' - n) s'
    in  (if r < 0 then "-" else "") ++ h ++ "." ++ f
-- The length of the repeating digits is related to the totient function of the denominator.
-- This means that the complexity of computing them is at least as bad as factoring, i.e., it quickly becomes infeasible.
showRational Nothing r =
    let (i, f) = properFraction (abs r) :: (Integer, Rational)
        si = if r < 0 then "-" ++ show i else show i
        decimals f = loop f [] ""
        loop x fs ds =
            if x == 0 then
                ds
            else
                case findIndex (x ==) fs of
                    Just i  -> let (l, r) = splitAt i ds in l ++ "(" ++ r ++ ")"
                    Nothing -> let (c, f) = properFraction (10 * x) :: (Integer, Rational) in loop f (fs ++ [x]) (ds ++ show c)
    in  if f == 0 then si else si ++ "." ++ decimals f