Я не понимаю преобразования чисел в Haskell
вот что я пытаюсь сделать:
isPrime :: Int -> Bool
isPrime x = all (y -> x `mod` y /= 0) [3, 5..floor(sqrt x)]
(Я знаю, что не проверяю деление на два-Пожалуйста, игнорируйте это.) Вот что я получаю:
No instance for (Floating Int)
arising from a use of `sqrt'
Possible fix: add an instance declaration for (Floating Int)
In the first argument of `floor', namely `(sqrt x)'
In the expression: floor (sqrt x)
In the second argument of `all', namely `[3, 5 .. floor (sqrt x)]'
Я потратил буквально часы, пытаясь все, что я могу придумать, чтобы сделать этот список, используя какой-то вариант sqrt, включая ерунду, такую как
intSqrt :: Int -> Int
intSqrt x = floor (sqrt (x + 0.0))
кажется, что (sqrt 500) отлично работает, но (sqrt x) настаивает на том, чтобы x был плавающим (почему?), и нет функции, которую я могу найти, чтобы преобразовать Int в реальный (почему?).
Я не хочу метод для проверки примитивности, я хочу понять, как это исправить. Почему это так трудно?
2 ответов
В отличие от большинства других языков, Haskell строго различает интегральные и типы с плавающей запятой и не будет преобразовывать один в другой неявно. См.здесь для того, как сделать преобразование явно. Есть даже sqrt
пример :-)
основная причина этого заключается в том, что сочетание неявных преобразований и системы классов Хаскеля (довольно сложной, но очень крутой) сделало бы реконструкцию типов очень сложной-вероятно, это растянуло бы ее за пределами точки, где это вообще может быть сделано машинами. Языковые дизайнеры считали, что получение классов типов для арифметики стоит затрат на явное указание преобразований.
ваша проблема в том, что, хотя вы пытались исправить это различными способами, вы не пытались что-то сделать x
, именно в этом и заключается ваша проблема. Давайте посмотрим на тип sqrt
:
Prelude> :t sqrt
sqrt :: (Floating a) => a -> a
С другой стороны, x
это Int
, и если мы спросим GHCi для информации о Floating
, он говорит нам:
Prelude> :info Floating
class (Fractional a) => Floating a where
pi :: a
<...snip...>
acosh :: a -> a
-- Defined in GHC.Float
instance Floating Float -- Defined in GHC.Float
instance Floating Double -- Defined in GHC.Float
Итак, единственные типы, которые являются Floating
are Float
s и Double
s. Нам нужен способ преобразовать Int
до Double
, сколько floor :: (RealFrac a, Integral b) => a -> b
идет в другую сторону. Всякий раз, когда у вас есть вопрос типа, как это, вы можете задать Hoogle, поисковая система Haskell, которая ищет типы. К сожалению, если вы ищете Int -> Double
, вы получаете паршивые результаты. Но что если мы расслабимся, что мы ищем? Если мы ищем Integer -> Double
, мы находим, что есть функция fromInteger :: Num a => Integer -> a
, что почти точно то, что вы хотите. И если мы расслабим наш тип до конца ... (Integral a, Num b) => a -> b
, вы обнаружите, что существует функция fromIntegral :: (Integral a, Num b) => a -> b
.
таким образом, чтобы вычислить квадратный корень целого числа, используйте floor . sqrt $ fromIntegral x
, или использовать
isqrt :: Integral i => i -> i
isqrt = floor . sqrt . fromIntegral
вы думали о проблеме в правильном направлении для вывода sqrt
; он возвращает числа с плавающей запятой, но вы хотели целое. Однако в Haskell нет понятия подтипа или неявных приведений, поэтому вам нужно изменить вход до sqrt
as что ж.
чтобы решить некоторые из ваших других проблем:
(+) типаintSqrt :: Int -> Int intSqrt x = floor (sqrt (x + 0.0))
Num a => a -> a -> a
- вы можете добавить только две вещи одного типа. Это вообще хорошо, так как это означает, что вы не можете добавить комплексное число в реальную матрицу 5×5; однако, так как 0.0
должен быть экземпляр Fractional
, вы не сможете добавить его в x :: Int
.
кажется ,что (sqrt 500) отлично работает...
это работает, потому что типа 500
не то, что вы ожидаете. Давайте спросим нашего верного компаньона GHCi:
Prelude> :t 500
500 :: (Num t) => t
фактически, все целочисленные литералы имеют этот тип; они могут быть любой вид числа, который работает, потому что Num
класс содержит функцию fromInteger :: Integer -> a
. Поэтому, когда вы написали sqrt 500
, GHC понял, что 500
нужно, чтобы удовлетворить 500 :: (Num t, Floating t) => t
(и он будет неявно выбирать Double
для числовых типов, как это спасибо правила недобросовестный). Аналогично,0.0
выше типа Fractional t => t
, спасибо Fractional
' s