Сравнение интегральных и плавающих значений - PullRequest
2 голосов
/ 11 декабря 2010

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

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

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

Ответы [ 3 ]

5 голосов
/ 11 декабря 2010

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

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

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

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

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

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

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

5 голосов
/ 11 декабря 2010

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

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

0 голосов
/ 11 декабря 2010

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

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

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

...