Чрезмерное использование Integral в Haskell
Всякий раз, когда я пишу функцию с использованием удвоений и целых чисел, я нахожу эту проблему, когда мне постоянно приходится использовать "fromIntegral" всюду в моей функции. Например:
import Data.List
roundDouble
:: Double
-> Int
-> Double
roundDouble x acc = fromIntegral (round $ x * 10 ** fromIntegral acc) / 10 ** fromIntegral acc
Есть ли более простой способ написать это? (Я знаю, что могут быть более простые способы округления числа, и если есть, пожалуйста, дайте мне знать! Однако меня в основном интересует, как избежать использования так много "fromIntegrals".)
Спасибо, Ash
Ответы
Ответ 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)