Страницы

Поиск по вопросам

понедельник, 24 февраля 2020 г.

Округление до 2 знаков после запятой Haskell

#haskell


Недавно начал пробовать Haskell на примере маленьких задачек - хеллоуворлдов. Сама
задача состоит в том, что нужно посчитать некоторое выражение при разных x - ничего
сложного. Но для красоты решил округлить ответ до 2 знаков после запятой и тут столкнулся
со сложностями. Так как стандартной реализации такого округления не нашел, завелосипедил
свою:

main :: IO ()        
main = do 
           let x = 1.7
               in putStrLn $ show $ roundTo2 ((x + 1) ^ 2 + 3 * (x + 1))

roundTo2 :: Num a => a -> Float
roundTo2 x = truncate (x * 100) / 100


Ругань компилятора:

Could not deduce (RealFrac a) arising from a use of \`truncate'
    from the context (Num a)
      bound by the type signature for roundTo2 :: Num a => a -> Float
      at prog.hs:6:13-31
    Possible fix:
      add (RealFrac a) to the context of
        the type signature for roundTo2 :: Num a => a -> Float
    In the first argument of \`(/)', namely \`truncate (x * 100)'
    In the expression: truncate (x * 100) / 100
    In an equation for `roundTo2':
        roundTo2 x = truncate (x * 100) / 100


Понимаю что проблема где-то в типах, но не понимаю, как эту проблему решить
    


Ответы

Ответ 1



Используйте функцию printf из модуля Text.Printf. import Text.Printf main :: IO () main = do let x = 1.7 printf "%.2f\n" ((x + 1) ^ 2 + 3 * (x + 1))

Ответ 2



Во-первых, можно воспользоваться советом из текста об ошибке: добавить класс (RealFrac a) в определение типа функции. (Num a) при этом можно убрать: RealFrac включает в себя Num. Во-вторых, можно воспользоваться ghci и подсмотреть тип функции: GHCi, version 7.10.1: http://www.haskell.org/ghc/ :? for help Prelude> let roundTo2 x = truncate (x * 100) / 100 Prelude> :t roundTo2 roundTo2 :: (Fractional a, Integral a, RealFrac a1) => a1 -> a

Ответ 3



Поборол ошибку следующим образом: main :: IO () main = do let x = 1.7 f = (x + 1) ^ 2 + 3 * (x + 1) in print $ roundTo2 f roundTo2 :: (RealFrac a) => a -> a roundTo2 x = fromIntegral f / 100 where f = round (x * 100) Проблема оказалась в функции деления (/) а не truncate, как я предполагал(Кстати в итоговом решении заменил на round как более подходящую). Суть проблемы в том, что деление в Haskell определено для дробных аргументов, принадлежащих классу типов Fractional: :t (/) (/) :: Fractional a => a -> a -> a Но truncate(и round) возвращают значение целочисленного типа Integral: :t round round :: (RealFrac a, Integral b) => a -> b Для решения задачи я воспользовался функцией fromIntegral, которая приводит значение целочисленного типа к значению произвольного числового типа Num, подклассом которого является класс Fractional: :t fromIntegral fromIntegral :: (Num b, Integral a) => a -> b

Комментариев нет:

Отправить комментарий