#haskell
Вопрос:
Я не хочу выполнять целочисленное деление. Я хочу ввести дробный тип ввода, выполнить с ним деление, округлить результат до целого числа и вернуть его.
piCalc :: (Fractional a, Ord a, Integral b) => a -> (a, b)
piCalc z = (piCalc' steps 1 0, steps)
where steps = round $ (4-z)/(2*z) 1
piCalc' :: (Integral a, Ord a, Fractional b) => a -> a -> a -> b
piCalc' 0 s k = 0
piCalc' steps s k = (fromInteger s*4)/(fromInteger 2*k 1) piCalc' (steps-1) ((-1)*s) (k 1)
main = do
print (piCalc 0.001)
Я получаю следующую ошибку:
Recursion.hs:59:19: error:
* Could not deduce (RealFrac a) arising from a use of `round'
from the context: (Fractional a, Ord a, Integral b)
bound by the type signature for:
piCalc :: forall a b.
(Fractional a, Ord a, Integral b) =>
a -> (a, b)
at Recursion.hs:57:1-58
Possible fix:
add (RealFrac a) to the context of
the type signature for:
piCalc :: forall a b.
(Fractional a, Ord a, Integral b) =>
a -> (a, b)
* In the first argument of `($)', namely `round'
In the expression: round $ (4 - z) / (2 * z) 1
In an equation for `steps': steps = round $ (4 - z) / (2 * z) 1
|
59 | where steps = round $ (4-z)/(2*z) 1
| ^^^^^
Recursion.hs:63:34: error:
* Couldn't match expected type `Integer' with actual type `a'
`a' is a rigid type variable bound by
the type signature for:
piCalc' :: forall a b.
(Integral a, Ord a, Fractional b) =>
a -> a -> a -> b
at Recursion.hs:61:1-64
* In the first argument of `fromInteger', namely `s'
In the first argument of `(*)', namely `fromInteger s'
In the first argument of `(/)', namely `(fromInteger s * 4)'
* Relevant bindings include
k :: a (bound at Recursion.hs:63:17)
s :: a (bound at Recursion.hs:63:15)
steps :: a (bound at Recursion.hs:63:9)
piCalc' :: a -> a -> a -> b (bound at Recursion.hs:62:1)
|
63 | piCalc' steps s k = (fromInteger s*4)/(fromInteger 2*k 1) piCalc' (steps-1) ((-1)*s) (k 1)
| ^^^
Изменение дробного на RealFrac в piCalc (верхняя функция) исправило для меня первую ошибку, но я не понимаю, почему и не знаю, является ли это «хорошим» решением. Я едва мог найти примеры использования RealFrac при поиске в Google. Я не знаю, что вызывает вторую ошибку.
Ответ №1:
RealFrac
Ограничение подразумевает как Real
и Fractional
. Real
означает, что числа должны быть 1-мерными (в отличие, скажем, от 2-мерных комплексных чисел или векторов более высокой размерности). round
требуется Real
, потому что он должен отображать свои входные данные на 1-мерном наборе целых чисел. И round
, очевидно , требует Fractional
, иначе нет особого смысла округлять. Итак, round
требуется a RealFrac
в качестве входных данных. Изменение Fractional
на RealFrac
-это правильное решение первой ошибки.
Вторая ошибка заключается в том , что вы указываете, что ваши типы являются Integral
, но затем применяете fromInteger
функцию, которая ожидает конкретного Integer
. Тебе следует переодеться fromInteger
в fromIntegral
.
Тогда вы все равно получите ошибку, потому что вы пишете fromInteger 2*k
, что означает (fromInteger 2) * k
на языке Хаскелл, но вы, вероятно, имели в виду fromInteger (2 * k)
или 2 * fromInteger k
. И это также следует изменить на fromIntegral
.
Этот код компилируется без ошибок:
piCalc :: (RealFrac a, Ord a, Integral b) => a -> (a, b)
piCalc z = (piCalc' steps 1 0, steps)
where steps = round $ (4-z)/(2*z) 1
piCalc' :: (Integral a, Ord a, Fractional b) => a -> a -> a -> b
piCalc' 0 s k = 0
piCalc' steps s k = (fromIntegral s*4)/(2 * fromIntegral k 1) piCalc' (steps-1) ((-1)*s) (k 1)
main = do
print (piCalc 0.001)
Если вы просто заинтересованы в вычислении pi, тогда, возможно, имеет смысл выбрать более конкретные типы, например Double
, или Rational
вместо RealFrac a => a
и Int
или Integer
вместо Integral a => a
. Вы можете сохранить реализацию прежней и изменить только подписи:
piCalc :: Double -> (Double, Int)
piCalc z = (piCalc' steps 1 0, steps)
where steps = round $ (4-z)/(2*z) 1
piCalc' :: Int -> Int -> Int -> Double
piCalc' 0 s k = 0
piCalc' steps s k = (fromIntegral s*4)/(2 * fromIntegral k 1) piCalc' (steps-1) ((-1)*s) (k 1)
main = do
print (piCalc 0.001)