Сравнение интегрального и плавающего значений

Итак, я нахожусь в процессе изучения 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.

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

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


person Shrikant Sharat    schedule 11.12.2010    source источник


Ответы (3)


Ваша проблема в том, что... ну... вы не можете сравнивать целочисленные и плавающие значения :-) Вы должны явно указать преобразования между целыми числами и числами с плавающей запятой. Функция 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, чтобы преобразовать наше значение с плавающей запятой обратно в целое число (обратите внимание, что это округление в меньшую сторону!).

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

person Joey Adams    schedule 11.12.2010
comment
вау, так много для так мало. Я пытался использовать floor $ sqrt, но я не ставил fromIntegral, думаю, нужно привыкнуть к этому :). И да, я тоже должен ставить подписи шрифтов :) - person Shrikant Sharat; 11.12.2010

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

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

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

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

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

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

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

person Carl    schedule 11.12.2010
comment
Ах, большое спасибо, Карл, у меня действительно было концептуальное различие между типами и классами типов (я читаю книгу «Изучите Haskell для великой пользы»), но когда я сказал значение Floating, я имел в виду значение с типом, который является экземпляр класса типа Floating. Привыкаю к ​​терминологии :) - person Shrikant Sharat; 11.12.2010

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

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

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

person Michael Kohl    schedule 11.12.2010
comment
Спасибо, думаю, мне следует перестать относиться к Haskell как к динамическому языку и начать добавлять сигнатуры типов :) - person Shrikant Sharat; 11.12.2010
comment
Вам, конечно, не нужно, вывод типов в Haskell действительно хорош. Я просто обнаружил, что это помогает мне думать о моем коде. - person Michael Kohl; 11.12.2010