Haskell: как правильно отображать процент до двух десятичных знаков?

В программе я вычисляю процент и печатаю его пользователю. Проблема заключается в том, что процентный отпечаток отображается со слишком большим количеством десятичных знаков. Я искал функцию, решая эту проблему, не нашел ее и запрограммировал функцию округления ниже, но мне интересно, есть ли более стандартный способ сделать это. Кроме того, идея, описанная в этой теме Java, является довольно опрятной, и если уже есть функции для этого, я хотел бы знать.

roundTo :: (Integral int1, Integral int2) => int1 -> Double -> Either int2 Double
roundTo digitsAfterComma value
    | (digitsAfterComma >= 1) =
        let
            factor = fromIntegral (10 ^ digitsAfterComma)
            result = ((/ factor) . fromIntegral . round . (* factor)) value
        in
            Right result
    | (digitsAfterComma == 0) = Left (round value)
    | otherwise = error "minimal precision must be non-negative"

Обновление. Ниже приведена более полная версия, разработанная благодаря полученным ответам.

import qualified Text.Printf as T

showPercentage :: (Integral a, Show a) => a -> Double -> String
showPercentage digitsAfterComma fraction
    | (digitsAfterComma >= 1) =
        let formatString = "%." ++ (show digitsAfterComma) ++ "f%%"
        in  T.printf formatString percentage

    | (digitsAfterComma == 0) =
        let formatString = "%d%%"
        in  T.printf formatString (round percentage :: Int)

    | otherwise = error "minimal precision must be non-negative"
    where percentage = 100 * fraction

Ответ 1

> import Text.Printf
> printf "%.2f" 0.22324 :: String
"0.22"

Вы можете использовать большинство строк формата C printf.

Имейте в виду, однако, что Haskell printf включает в себя несколько сложных машин типа typeclass и может генерировать трудночитаемые ошибки типа. Он также очень общий, поскольку он также может возвращать IO-действия, т.е.

> printf "%.2f" 0.22324 :: IO ()
0.22

не возвращает строку, но напрямую печатает ее. Я бы рекомендовал вам всегда добавлять аннотацию типа (например, :: String выше) после каждого вызова printf, если только он не очистит из контекста то, что является типом возврата (например, в блоке do с другими действиями IO).

Ответ 2

printf является твердой, а другая библиотека, о которой стоит знать, formatting (которая основана на прекрасном HoleyMonoid библиотека):

Prelude Formatting> format ("to two decimal places: " % prec 2 % "!") 0.2222
"to two decimal places: 0.22!"

Обратите внимание, что formatting безопасен по типу, в отличие от printf:

Prelude Text.Printf Formatting> :t printf "%.2f" "hi"
printf "%.2f" "hi" :: PrintfType t => t
Prelude Text.Printf Formatting> printf "%.2f" "hi"
*** Exception: printf: bad formatting char 'f'

Prelude Text.Printf Formatting> :t format (prec 2) "hi"
-- type error