Я не понимаю преобразования чисел в Хаскеле - PullRequest
12 голосов
/ 09 августа 2011

Вот что я пытаюсь сделать:

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 ]

21 голосов
/ 09 августа 2011

В отличие от большинства других языков, Haskell строго различает целочисленные типы и типы с плавающей точкой и не будет неявно преобразовывать один в другой.Смотрите здесь , чтобы узнать, как выполнить преобразование явно.Есть даже sqrt пример: -)

Основная причина этого заключается в том, что сочетание неявных преобразований и системы классов Хаскеля (довольно сложной, но очень классной) сделало бы реконструкцию типов очень трудной - возможно, эторастяните его дальше, чем это может быть сделано машинами.Разработчики языка чувствовали, что получение классов типов для арифметики стоило того, чтобы явно указывать преобразования.

19 голосов
/ 10 августа 2011

Ваша проблема в том, что, хотя вы пытались исправить это различными способами, вы не пытались что-то сделать 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, являются Float с и Double с. Нам нужен способ конвертировать 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, однако, нет понятия подтипов или неявных приведений, поэтому вам также нужно изменить input на sqrt.

Для решения некоторых других ваших проблем:

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 fromRational :: Rational -> a.

… но (sqrt x) настаивает на том, чтобы x был плавающим…

Смотрите выше, где мы смотрим на тип sqrt.

… и я не могу найти функцию для преобразования Int в реальное….

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

Почему это так сложно?

Я надеюсь, что это больше не так, теперь, когда у вас есть fromIntegral.

...