Я не понимаю преобразования чисел в 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 Floats и Doubles. Нам нужен способ преобразовать 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