#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
Комментариев нет:
Отправить комментарий