Задача определения базовой функции Haskell - PullRequest
5 голосов
/ 06 марта 2011

Я изучаю Haskell и пытаюсь написать функцию, которая возвращает список факторов для числа.Вот что у меня есть:

factors :: Int -> [Int]
factors n = [x | x <- [2..s], n `mod` x == 0]
    where s = floor (sqrt n)

Когда я пытаюсь загрузить модуль в ghci, я получаю две ошибки:

p003.hs:3:14:
    No instance for (RealFrac Int)
      arising from a use of `floor' at p003.hs:3:14-27
    Possible fix: add an instance declaration for (RealFrac Int)
    In the expression: floor (sqrt n)
    In the definition of `s': s = floor (sqrt n)
    In the definition of `factors':
        factors n = [x | x <- [2 .. s], n `mod` x == 0]
                  where
                      s = floor (sqrt n)

p003.hs:3:21:
    No instance for (Floating Int)
      arising from a use of `sqrt' at p003.hs:3:21-26
    Possible fix: add an instance declaration for (Floating Int)
    In the first argument of `floor', namely `(sqrt n)'
    In the expression: floor (sqrt n)
    In the definition of `s': s = floor (sqrt n)
Failed, modules loaded: none.

Есть предложения?

Ответы [ 2 ]

10 голосов
/ 06 марта 2011

Параметр имеет тип Int, поэтому для него нельзя вычислить квадратный корень. Сначала нужно преобразовать его в тип с плавающей запятой, что можно сделать с помощью fromIntegral. В отличие от некоторых других языков, Haskell не переводит целые числа в числа с плавающей запятой автоматически (как и никакие другие автоматические преобразования типов).

Так что измените sqrt n на sqrt (fromIntegral n).

9 голосов
/ 06 марта 2011

причина проблемы

Тип функции sqrt:

sqrt :: (Floating a) => a -> a

Вы можете проверить это, набрав :t sqrt в ghci.

Int не является экземпляром Floating, поэтому вы видите вторую ошибку.

Причина первой ошибки та же; проверка :t floor показывает, что тип:

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

Функция ожидает экземпляр RealFrac, и вы предоставляете Int.

Ввод :info RealFrac или :info Floating показывает, что ни у одного из них нет экземпляра для Int, поэтому в тексте ошибки указано

Нет экземпляра для ... Int


Решение

Решение этой проблемы - убедиться в правильности типов; они должны быть членами соответствующих классов типов.

Простой способ сделать это - использовать функцию fromIntegral, которая, как показывает :t, имеет тип:

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

Использование fromIntegral необходимо, поскольку входящий тип - Int, но функции floor и sqrt работают с типами RealFrac и Floating соответственно.

Это разрешено, потому что, как видно из сигнатуры типа, fromIntegral возвращает экземпляр Num, который включает в себя оба типа: RealFrac и Floating. Вы можете убедиться в этом, набрав :info Num и :info Float в ghci и просмотрев вывод.

Внесение изменений в вашу программу будет иметь следующий результат, который будет работать так, как вы хотите:

factors :: Int -> [Int]
factors n = [x | x <- [2..s], n `mod` x == 0] 
    where s = floor (sqrt $ fromIntegral n) 

Дальнейшее чтение

Два хороших ресурса для точного понимания происходящего - разделы учебника по Haskell по Классам типов и Числа .

...