在Haskell中获取数字除数列表的问题

时间:2011-05-05 02:38:56

标签: haskell types floating-point integer

这不是重复的问题。请阅读以下内容......

我正在声明以下功能:

divisors x = [(a, x/a) | a <- [2..(sqrt x)], x `mod` a == 0]

我想要获得的是x的除数:包含(n, k)的元组列表,例如n * k = x

示例:

> divisors x
[(1,10), (2, 5)]

为什么上面的代码不起作用?

它给了我错误:

*Main> divisors 10

<interactive>:1:0:
    Ambiguous type variable `t' in the constraints:
      `Floating t'
        arising from a use of `divisors' at <interactive>:1:0-10
      `Integral t'
        arising from a use of `divisors' at <interactive>:1:0-10
    Probable fix: add a type signature that fixes these type variable(s)

我尝试手动设置功能的签名但没有成功......

4 个答案:

答案 0 :(得分:4)

问题是sqrt返回Floating a,你真的只想在找到除数时使用整数。您可以使用Floating aIntegral aceilingfloor变为round。我将使用ceiling,因为我不确定使用flooraverage是否会跳过除数。

sqrt函数也只接受浮点数,因此您必须先将整数转换为浮点数(然后才能将其赋予它)(可以使用fromIntegral完成)。

此外,您使用/,它也适用于浮动数字。使用div更好,因为它适用于整数(必要时舍入)。

divisors x = [(a, x `div` a) | a <- [2..(ceiling $ sqrt $ fromIntegral x)], x `mod` a == 0]

有了这个,divisors 10会给[(2,5)](你的代码会阻止(1,10)案例的发生 - 我猜这是故意的)。不幸的是,您会收到重复项,例如divisors 12会返回[(2,6),(3,4),(4,3)],但如果这是一个问题,这不应该太难修复。

答案 1 :(得分:4)

如果您要求输入类型,可以看到问题:

 divisors :: (Integral t, Floating t) => t -> [(t, t)]

然后检查IntegralFloating

的内容
 Prelude> :info Floating
 class Fractional a => Floating a where
 instance Floating Float -- Defined in GHC.Float
 instance Floating Double -- Defined in GHC.Float

 Prelude> :info Integral
 class (Real a, Enum a) => Integral a where
 instance Integral Integer -- Defined in GHC.Real
 instance Integral Int -- Defined in GHC.Real

所以,它既不是Int,Integer,Float也不是Double。你遇到了麻烦......

值得庆幸的是,我们可以在不同类型之间进行转换,因此虽然sqrt需要浮动,而mod需要积分(顺便说一下,rem更快),我们也可以,例如,取消浮点除法:

 divisors :: Integer -> [(Integer, Integer)]
 divisors x = [(a, x `div` a) | a <- [2..ceiling (sqrt (fromIntegral x))], x `rem` a == 0]

 > divisors 100
 [(2,0),(4,0),(5,0),(10,0)]

但是,您需要仔细考虑将整数类型转换为浮点数时的真正意义,通过sqrt ...

答案 2 :(得分:3)

在Haskell中,整数除法和小数除法是不同的操作,并且具有不同的名称。斜杠运算符/用于小数除法。整数除法由divquot完成(当涉及负数时,两者之间的差异与行为有关)。

尝试用

替换x/a
x `quot` a

代替。

编译器错误正好告诉您:您有时将某种类型视为整数(使用mod),有时将其视为小数(使用/),并且它不确定如何选择一种类似于这两者的类型。

但是,一旦排序,您将遇到与sqrt类似的问题。同样,您需要确定您的类型是整数还是(在这种情况下)浮点。为了找到可能的除数,它应该足以达到小于浮点的最大整数,因此请考虑使用floor (sqrt (fromIntegral x)))fromIntegralx(必须具有整数类型)转换为其他类型 - 在这种情况下,它将默认为Doublefloor然后将Double结果转换回整数类型。

答案 3 :(得分:1)

不是采用平方根来绑定搜索,而是允许理解范围超出无限列表,并且当余数大于除数时使用takeWhile来停止搜索:

divisors x = takeWhile (uncurry (<=)) [(a, x `div` a) | a <- [1..], x `mod` a == 0]

> divisors 100
[(1,100),(2,50),(4,25),(5,20),(10,10)]

注意:您的原始示例将(1,10)显示为divisors的{​​{1}}之一,因此我从10而不是1开始理解。< / p>

嗯,这会搜索超出平方根,直到它达到上面的下一个因素。

这个怎么样:

2