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

发布于 2024-11-05 07:37:21 字数 774 浏览 0 评论 0原文

这不是重复的问题。请阅读下文...

我声明以下函数:

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)

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

This is not a duplicate question. Read below...

I'm declaring the following function:

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

What I want to obtain is the divisors of x: A list of tuples that will contain (n, k) such as n * k = x

Example:

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

Why the above code isn't working?

It gives me the error:

*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)

I've tried manually setting the signature of the function without success...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

国产ˉ祖宗 2024-11-12 07:37:21

问题是 sqrt 返回一个 Floating a,而在查找除数时您实际上只需要整数。您可以将浮动 a 转换为带有 ceilingfloorroundIntegral a >。我将使用 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)],但这不应该如果出现问题就很难修复。

The problem is sqrt returns a Floating a, and you really just want integers when finding divisors. You can turn a Floating a into an Integral a with ceiling, floor or round. I will use ceiling, as I'm not sure if using floor or average won't skip a divisor.

The sqrt function also only accepts a floating number, so you will have to convert an integer into a floating before giving it to it (this can be done with fromIntegral).

Also, you use /, which also works with floating numbers. Using div is better as it works with integral numbers (rounding when necessary).

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

With this, divisors 10 will give [(2,5)] (your code stops the (1,10) case from happening - I'm guessing this was intentional). Unfortunately you will get duplicates, eg divisors 12 will return [(2,6),(3,4),(4,3)], but that shouldn't be too hard to fix if it is a problem.

潦草背影 2024-11-12 07:37:21

如果您询问类型,您就可以看到问题:

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

然后检查哪些东西既是 Integral 又是 Floating :

 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 也不能​​是双倍的。你有麻烦了...

值得庆幸的是,我们可以在类型之间进行转换,因此虽然 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)]

但是,您需要认真思考在通过 sqrtsqrt 将整数类型转换为浮点时您真正想要做什么代码>...

You can see the problem if you ask for the type:

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

and then check what things are both Integral and Floating:

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

and

 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

so, it can be neither Int, Integer, Float or Double. You're in trouble...

Thankfully, we can convert between types, so that while sqrt needs a Floating, and mod needs an Integral (btw, rem is faster), we can either, e.g., do away with floating point division:

 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)]

However, you need to think hard about what you really mean to do when converting integer types to floating point, via sqrt...

迷鸟归林 2024-11-12 07:37:21

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

尝试用 x/a 替换

x `quot` a

编译器错误准确地告诉您这一点:您有时将类型视为整数(通过使用 mod),有时将类型视为小数(通过使用 / ),并且不确定如何选择一种与这两种行为类似的类型。

不过,一旦排序完毕,您将在 sqrt 上遇到类似的问题。同样,您需要对您的类型是整数还是(在这种情况下)浮点数保持一致。为了找到可能的除数,范围小于浮点数的最大整数就足够了,因此请考虑使用floor (sqrt (fromIntegral x)))。 fromIntegralx(必须具有整数类型)转换为不同的类型 - 在本例中,它将默认为 Double。然后,floorDouble 结果转换回整数类型。

In Haskell, integer division and fractional division are different operations, and have different names. The slash operator, /, is for fractional division. Integer division is accomplished with div or quot (the difference between the two having to do with the behavior when there are negative numbers involved).

Try replacing x/a with

x `quot` a

instead.

The compiler error tells you exactly this: that you're treating a type sometimes as an integral number (by using mod), and sometimes as a fractional number (by using /), and it's not sure how to pick a type that acts like both of those.

You'll have a similar issue with sqrt, once that's sorted, though. There again, you need to be consistent about whether your types are integers or (in that case) floating point. For the purpose of finding possible divisors, it should suffice to range up to the greatest integer less that the floating point, so consider using floor (sqrt (fromIntegral x))). The fromIntegral converts x (which must have an integral type) to a different type -- in this case, it will default to Double. The floor then converts the Double result back into an integral type.

哥,最终变帅啦 2024-11-12 07:37:21

您可以允许推导式范围覆盖无限列表,而不是采用平方根来限制搜索,并在余数大于除数时使用 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)10除数之一,所以我从1开始理解> 代替<代码>2。

嗯,这确实会搜索超出平方根的范围,直到遇到上面的下一个因子。

这个怎么样:

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

Instead of taking the square-root to bound the search, you can allow the comprehension to range over an infinite list, and use takeWhile to stop the search when the remainder is greater than the divisor:

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)]

Note: your original example shows (1,10) as one of the divisors of 10, so I started the comprehension from 1 instead of 2.

Hmm, this does search beyond the square-root until it hits the next factor above.

How about this:

divisors x = [(a, x `div` a) | a <- takeWhile ((<= x) . (^2)) [1..], x `mod` a == 0]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文