Haskell, функция работает при использовании чисел, но не с переменными

#variables #haskell #types #literals

#переменные #haskell #типы #литералы

Вопрос:

Я использую ghci, и у меня возникла проблема с функцией для получения множителей числа.

Код, с которым я хотел бы поработать, это:

 let factors n = [x | x <- [1..truncate (n/2)], mod n x == 0]
  

Она не жалуется, когда я нажимаю enter, но как только я пытаюсь ее использовать (в данном случае с 66) Я получаю это сообщение об ошибке:

 Ambiguous type variable 't0' in the constraints:
  (Integral t0)
    arising from a use of 'factors' at <interactive>:30:1-10
  (Num t0) arising from the literal '66' at <interactive>:30:12-13
  (RealFrac t0)
    arising from a use of 'factors' at <interactive:30:1-10
  Probable fix: add a type signature that fixes these type variable(s)
  In the expression: factors 66
  In the equation for 'it': it = factors 66
  

Следующий код работает отлично:

 let factorsOfSixtySix = [x | x <- [1..truncate (66/2)], mod 66 x == 0]
  

Я новичок в haskell, и после просмотра типов и классов типов я все еще не уверен, что я должен делать.

Комментарии:

1. Угадайте, что бы n/2 вернулось!

Ответ №1:

Вместо этого используйте div для целочисленного деления:

 let factors n = [x | x <- [1.. n `div` 2], mod n x == 0]
  

Проблема в вашем коде заключается в том, что / требуется RealFrac тип для n в то время mod как Integral один. Это нормально во время определения, но тогда вы не сможете выбрать тип, который соответствует обоим ограничениям.

Другим вариантом могло бы быть усечение n перед использованием mod , но это более громоздко. В конце концов, вы не хотите вызывать factors 6.5 , не так ли? 😉

 let factors n = [x | x <- [1..truncate (n/2)], mod (truncate n) x == 0]
  

Ответ №2:

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

 GHCi> let factors n = [x | x <- [1..truncate (n/2)], mod n x == 0]
GHCi> :t factors
factors :: (Integral t, RealFrac t) => t -> [t]
GHCi> let { factors :: Double -> [Double]; factors n = [x | x <- [1..truncate (n/2)], mod n x == 0]; }

<interactive>:30:64:
    No instance for (Integral Double) arising from a use of `truncate'
    Possible fix: add an instance declaration for (Integral Double)
    In the expression: truncate (n / 2)
    In the expression: [1 .. truncate (n / 2)]
    In a stmt of a list comprehension: x <- [1 .. truncate (n / 2)]
GHCi> let { factors :: Integer -> [Integer]; factors n = [x | x <- [1..truncate (n/2)], mod n x == 0]; }

<interactive>:31:66:
    No instance for (RealFrac Integer) arising from a use of `truncate'
    Possible fix: add an instance declaration for (RealFrac Integer)
    In the expression: truncate (n / 2)
    In the expression: [1 .. truncate (n / 2)]
    In a stmt of a list comprehension: x <- [1 .. truncate (n / 2)]

<interactive>:31:77:
    No instance for (Fractional Integer) arising from a use of `/'
    Possible fix: add an instance declaration for (Fractional Integer)
    In the first argument of `truncate', namely `(n / 2)'
    In the expression: truncate (n / 2)
    In the expression: [1 .. truncate (n / 2)]
  

Ответ №3:

Я новичок в Haskell, поэтому, пожалуйста, простите мою смелость придумать ответ здесь, но недавно я сделал это следующим образом;

 factors :: Int -> [Int]
factors n = f'    [n `div` x | x <- tail f', x /= exc]
            where lim = truncate (sqrt (fromIntegral n))
                  exc = ceiling (sqrt (fromIntegral n))
                  f'  = [x | x <- [1..lim], n `mod` x == 0]
  

Я считаю, что это более эффективно. Вы заметите, если вам понравится;

 sum (factors 33550336)