Чрезмерное использование fromIntegral в Haskell - PullRequest
27 голосов
/ 11 августа 2010

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

import Data.List

roundDouble
    :: Double
    -> Int 
    -> Double
roundDouble x acc = fromIntegral (round $ x * 10 ** fromIntegral acc) / 10 ** fromIntegral acc

Есть ли более простой способ написать это?(Я знаю, что могут быть более простые способы округления числа, и если есть, пожалуйста, дайте мне знать! Однако меня больше всего интересует, как избежать использования столь многих «fromIntegrals».)

Спасибо, Эш

Ответы [ 4 ]

24 голосов
/ 11 августа 2010

Иногда я нахожу полезную вспомогательную функцию:

roundDouble x acc = (round $ x * 10 ^ acc) /. (10 ^ acc)
    where 
    x /. y = fromIntegral x / fromIntegral y

Эта вспомогательная функция также может быть записана:

(/.) = (/) `on` fromIntegral

Где on от Data.Function.

12 голосов
/ 11 августа 2010

Вы можете использовать ^ вместо **.^ принимает любой интеграл в качестве второго аргумента, поэтому вам не нужно вызывать fromIntegral для второго операнда.Таким образом, ваш код становится:

roundDouble x acc = fromIntegral (round $ x * 10 ^ acc) / 10 ^ acc

, который имеет только один fromIntegral.И тот, от которого вы не можете избавиться, так как round естественно возвращает Integral, и вы не можете выполнить нецелое деление на Integral.

6 голосов
/ 11 августа 2010

У меня похожая проблема с маршалинг-кодом, где fromIntegral используется для преобразования CInt в Int.Я обычно определяю fI = fromIntegral, чтобы было проще.Вам также может потребоваться дать ему явную сигнатуру типа или использовать -XNoMonomorphismRestriction.

Если вы много занимаетесь математикой, вы можете посмотреть на Числовое вступление , которое выглядитиметь гораздо более разумные отношения между различными числовыми типами.

4 голосов
/ 11 августа 2010

Другая идея, похожая на luqui's .Большинство моих проблем с fromIntegral связаны с необходимостью деления Int на Double или Double на Int.Таким образом, этот (/.) позволяет разделить любые два типа Real, не обязательно одинаковые, и не обязательно Integral типы, как в решении Люки:

(/.) :: (Real a, Real b, Fractional c) => a -> b -> c
(/.) x y = fromRational $ (toRational x) / (toRational y)

Пример:

ghci> let (a,b,c) = (2::Int, 3::Double, 5::Int)
ghci> (b/.a, c/.a, a/.c)
(1.5,2.5,0.4)

Это работает для любых двух Real с, но я подозреваю, что рациональное деление и преобразование в / из Rational не очень эффективно.

Теперь ваш пример становится:

roundDouble :: Double -> Int -> Double
roundDouble x acc = (round $ x * 10 ^ acc) /. (10 ^ acc)
...