2014-02-21 2 views
3

Я пытаюсь написать функцию, которая проверяет, является ли число простым. Я написал это:Haskell Ошибка «Нет экземпляра для»

primeCheck :: Int -> Int -> Bool 
primeCheck n i 
    | n == 2 = True 
    | i == 1 = True 
    | n `mod` i == 0 = False 
    | otherwise = primeCheck n (i -1) 

isPrime :: Int -> Bool 
isPrime n = primeCheck n (floor (sqrt n)) 

И я получаю эти ошибки:

Нет экземпляра для (RealFrac Int) , вытекающие из использования floor' Possible fix: add an instance declaration for (RealFrac Int) In the second argument of primeCheck 'а именно (floor (sqrt n))' In the expression: primeCheck n (floor (sqrt n)) In an equation for IsPrime': IsPrime п = primeCheck п (этаж (SQRT п))

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 n)' 
In the second argument of `primeCheck', namely `(floor (sqrt n))' 
In the expression: primeCheck n (floor (sqrt n)) Failed, modules loaded: none. 

Когда я изменить код, чтобы это многообещающе фи х проблема:

primeCheck :: Int -> Int -> Bool 
primeCheck n i 
    | n == 2 = True 
    | i == 1 = True 
    | n `mod` i == 0 = False 
    | otherwise = primeCheck n (i -1) 

isPrime :: Int -> Bool 
isPrime n = primeCheck n (floor (RealFrac (sqrt (Floating n)))) 

я получаю это:

Не в области видимости: конструктор данных `RealFrac '

Не в области видимости: данные конструктор` плавающей'

Как могу я исправить это?

ответ

7

Floating является typeclass, а не конструктор или функция. Вы, похоже, поняли, что вам нужно преобразовать тип n. Правильный способ сделать это будет использовать fromIntegral:

isPrime n = primeCheck n $ floor $ sqrt $ (fromIntegral n :: Double) 

Мы можем понять, почему это работает, следуя сигнатуры типов функций.

С типом подписи isPrime, мы видим n имеет тип Int.

Поскольку sqrt ожидает некоторый Floating типа (т.е. типа, который является экземпляром класса типов Floating), мы можем преобразовать из Int к Double с помощью fromIntegral. Обратите внимание, что подпись fromIntegral является

(Integral a, Num b) => a -> b 

Int является экземпляром Integral (поэтому тип входного сигнала в порядке) и Double является экземпляром Num поэтому тип вывода в порядке.

Затем мы берем sqrt, чтобы получить новый Double.

floor ожидает аргумент, тип которого является экземпляром RealFrac. Double является экземпляром как Floating, так иRealFrac, поэтому он выполнит работу (без необходимости преобразования). floor преобразует квадратный корень обратно в тип Int.

Обратите внимание, что, так как тип выхода из fromIntegral полиморфный, как тип вход sqrt и floor, мы должны указать тип преобразования как Double, иначе компилятор не будет знать, которыйNum/Floating/RealFrac экземпляр для преобразования в. Возможно, вы заметили ошибку ambiguous type 'a' in ....

Вы можете увидеть сигнатуры типов многих функций с помощью Hoogle

EDIT

Получается явный тип подписи для fromIntegral является не требуется. Таким образом,

isPrime n = primeCheck n $ floor $ sqrt $ fromIntegral n 

хватает. На мой взгляд, было бы яснее просто предоставить явную подпись, но в этом случае это не обязательно. Вы можете узнать больше об этом here.

+0

На самом деле, после тестирования как в GHCi, так и в GHC, я обнаружил, что явная подпись типа приведения * не требуется, хотя я не знаю почему (особенно для GHC, поскольку GHCi имеет некоторые смешные правила дефолта). – crockeea

+0

GHC имеет почти все правила по умолчанию; GHCi просто слегка смягчил условия, при которых он будет использовать типы по умолчанию, и по умолчанию некоторые вещи будут ('). См. Http://www.haskell.org/ghc/docs/7.6.3/html/users_guide/interactive-evaluation.html#extended-default-rules. Вы просто заметили это больше в GHCi, потому что компиляция одной строки за раз означает GHCi не может использовать весь модуль для вывода правильного типа, и поэтому выбор по умолчанию, а не тип выбирает ваши типы чаще. – Ben

2

Проблема заключается в преобразовании из Int для использования в sqrt. Измените последнюю строку

isPrime n = primeCheck n (floor (sqrt (fromIntegral n))) 
2

Floating и RealFrac являются тип-классы, а не функции.

Эта страница дает хороший обзор стандартной иерархии Haskell числового типа класса (раздел 6.3):

http://www.haskell.org/onlinereport/basic.html

В частности, эта диаграмма типа классов очень полезно:

http://www.haskell.org/onlinereport/classes.gif

Вы явно хотите взять квадратный корень из Int с помощью плавающей точкой sqrt и floor функций. Проблема состоит в том, что эти функции имеют следующие типы:

sqrt :: Floating a => a -> a 
floor :: (Integral b, RealFrac a) => a -> b 

Препятствия:

  1. sqrt принимает Floating аргумент, и Int не в классе Floating типа
  2. floor требует RealFrac аргумент, но sqrt производит только Floating

Вы можете решить первую с функцией fromIntegral:

fromIntegral :: (Integral a, Num b) => a -> b 

, который позволит вам конвертировать Int (или любой другой интегральный тип) в любой Num типа (который включает в себя Floating.)

Далее, глядя на диаграмму иерархии числовой тип класса мы видим, что Double и Float являются членами обоих типов классов Floating и RealFrac.

Так явный типа принуждение к Double вы можете применить floor:

isqrt :: Int -> Int 
isqrt n = floor (sqrt (fromIntegral n) :: Double) 

Теперь, как уже упоминалось @lthread, вы можете уйти с:

isqrt :: Int -> Int 
isqrt n = (floor . sqrt . fromIntegral) n 

и GHC будет по умолчанию возврата тип sqrt до Double. Это прекрасно, но вы должны знать, что это происходит. Например, если вы хотите использовать этот подход для получения целочисленного квадратного корня из Integer, вы хотите пройти через реальный тип abritrary точности (например, Data.Number.BigFloat) вместо Double.

Смежные вопросы