2010-12-11 4 views
2

Итак, я участвую в процессе обучения Haskell и часто падаю на ошибки типа/типа. Некоторые довольно очевидные глупые ошибки, а некоторые, которые заставляют меня чувствовать, что haskell не подходит для меня. Во всяком случае, у меня есть этот кусок коды ...Сравнение интегральных и плавающих значений

pfactors' ps n 
    | p > (sqrt n) = [] 
    | m == 0 = p : (pfactors' ps q) 
    | otherwise = pfactors' (tail ps) n where 
     p = head ps 
     (q, m) = divMod n p 

pfactors = pfactors' primes 

main = print $ pfactors 14 

(Некоторый фон:. Функция pfactors предполагается принять ряд и возвращает список простых чисел, которые являются главными факторами данного числа primes бесконечный список простых чисел)

, который дает мне эту ошибку:

p47.hs:10:11: 
    Ambiguous type variable `a' in the constraints: 
     `Floating a' arising from a use of `pfactors'' at p47.hs:10:11-26 
     `Integral a' arising from a use of `primes' at p47.hs:10:21-26 
    Possible cause: the monomorphism restriction applied to the following: 
     pfactors :: a -> [a] (bound at p47.hs:10:0) 
    Probable fix: give these definition(s) an explicit type signature 
        or use -XNoMonomorphismRestriction 

Теперь я понял, что это проблема с p < (sqrt n) части, потому что это единственная часть, которая не имеет ничего общего с Floating. Если я изменю его на p < n, все будет хорошо, и я получу правильный ответ. Но я действительно хочу проверить квадратный корень, так как мне это сделать?

И кстати, это не домашнее задание, если он чувствует, как, это моя попытка решения 47-й проблемы на projecteuler.net

Спасибо за любую помощь.

И, пожалуйста, не дайте мне решение указанной проблемы эйлера проекта, я хочу сделать это сам, насколько смогу :). Благодарю.

ответ

5

Ваша проблема в том, что ... ну ... вы не можете сравнивать интегральные и плавающие значения :-) Вы должны явно указывать конверсии между целыми числами и поплавками. Функция sqrt :: (Floating a) => a -> a работает с поплавками, но вы имеете дело прежде всего с целыми числами, поэтому вы не можете использовать ее бесплатно. Попробуйте что-то вроде этого:

pfactors' ps n 
    | p > (floor $ sqrt $ fromIntegral n) = [] 
    | m == 0 = p : (pfactors' ps q) 
    | otherwise = pfactors' (tail ps) n where 
     p = head ps 
     (q, m) = divMod n p 

Здесь мы используем fromIntegral :: (Integral a, Num b) => a -> b для преобразования целого числа в чем-то другом, что позволяет нам использовать его в качестве аргумента в sqrt. Затем мы скажем floor, чтобы преобразовать наше значение float обратно в целое число (обратите внимание, что это округляется!).

Во-вторых, я рекомендую привыкнуть добавлять подписи типов к вашим объявлениям верхнего уровня. Это не только даст вам лучшее понимание языка, но, если вы этого не сделаете, вы можете совершить фол monomorphism restriction.

+0

waw, так много для так мало. Я попытался использовать 'floor $ sqrt', но я не поставил' fromIntegral', нужно привыкнуть к этому материалу, я думаю :). И да, я тоже должен ставить подписи типов :) –

5

Я хочу дать некоторые дополнительные разъяснения о том, что происходит, даже если решение уже предоставлено.

В частности, классы типа НЕ. У вас не может быть значения Integral или Floating. Это не типы, они являются типами классов. Это не похоже на объектно-ориентированный подтипирование.

Подпись, такая как Integral a => a -> a -> a, не означает «функция, которая принимает два интегральных аргумента и возвращает некоторое интегральное значение». Это означает «Функция, которая принимает два значения типа a и возвращает значение типа a. Кроме того, тип a должен быть экземпляром Integral». Различия между ними значительны. Очень важно помнить, что оба аргумента и возвращаемое значение являются одинаковыми. «a» не может изменять свой смысл в сигнатуре типа.

Так что же все это значит?

Ну, во-первых, нет априорного требования, чтобы тип не мог быть экземпляром как интегральных, так и плавающих.Если бы у вас был такой тип, конверсии не требовались бы. Тем не менее, семантический смысл каждого из этих классов типов затрудняет для одного типа как пример как значимого.

Во-вторых, вы должны быть немного более осторожны в том, как вы говорите о типах классов. Они принципиально отличаются от объектно-ориентированного подтипирования. Из-за концептуальных различий существуют также терминологические различия. Быть точным с вашей терминологией помогает как понимать различия, так и передавать их другим.

Итак, как бы я сформулировал вопрос? Что-то вроде «Как преобразовать Integer в экземпляр Floating и обратно? Мне нужно использовать функцию sqrt :: Floating a => a -> a и сравнить его результат с целыми значениями».

+0

А, спасибо большое Карлу, у меня было концептуальное различие в типах и типах классов (я читаю книгу «Улыбаюсь вам Haskell для отличного»), но когда я сказал «Floating' value», я имел в виду «значение с типом, являющимся экземпляром класса типа« Floating ». Все еще привык к терминологии :) –

0

Вы уже получили свой ответ, но, возможно, это помогает избежать подобных проблем в функции:

http://www.haskell.org/haskellwiki/Converting_numbers

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

+0

Спасибо, я думаю, я должен прекратить рассматривать Haskell как динамический язык и начинать добавлять сигнатуры типов :) –

+0

Вам, конечно, не нужно, вывод типа Haskell действительно хорош. Я просто обнаружил, что это помогает мне думать о моем коде. –

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