Подтвердить что ты не робот

Чрезмерное использование Integral в Haskell

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

import Data.List

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

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

Спасибо, Ash

4b9b3361

Ответ 1

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

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

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

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

Где on находится от Data.Function.

Ответ 2

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

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

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

Ответ 3

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

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

Ответ 4

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

(/.) :: (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 s, но я подозреваю, что рациональное деление и преобразование в/из Rational не очень эффективны.

Теперь ваш пример будет выглядеть следующим образом:

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