因使用“it”而产生的不明确类型变量“a0”

Ambiguous type variable `a0' arising from a use of `it'

我有以下函数来return给定数字的因子对

factorPairs:: (RealFrac a, Floating a, Integral a) => a -> [(a, a)]
factorPairs n = map(\x -> (x, div n x)) [y | y <- [1..(ceiling $ sqrt n)], n `rem` y == 0]

当我在 ghci factorPairs 18 中调用函数时,我得到 运行 时间错误

   * Ambiguous type variable `a0' arising from a use of `it'
      prevents the constraint `(Floating a0)' from being solved.
      Probable fix: use a type annotation to specify what `a0' should be.
      These potential instances exist:
        instance Floating Double -- Defined in `GHC.Float'
        instance Floating Float -- Defined in `GHC.Float'
    * In the first argument of `print', namely `it'
      In a stmt of an interactive GHCi command: print it

我可以在 ghci 中对函数进行硬编码

map(\x -> (x, div 18 x)) [y | y <- [1..(ceiling $ sqrt 18)], 18 `rem` y == 0] 没有任何问题,但我似乎无法弄清楚为什么我的功能失败了。我相信 ghci 试图告诉我它无法弄清楚用什么类型调用 print 但我正在努力寻找解决方案。

这与 Haskell 中的数字文字超载有关。当您将 map(\x -> (x, div 18 x)) [y | y <- [1..(ceiling $ sqrt 18)], 18 `rem` y == 0] 键入 ghci 时,作为 sqrt 参数的 18 默认为 Double,其他为 Integer

然而,当你写

factorPairs:: (RealFrac a, Floating a, Integral a) => a -> [(a, a)]
factorPairs n = map(\x -> (x, div n x)) [y | y <- [1..(ceiling $ sqrt n)], n `rem` y == 0]

您强制 n 的所有实例只有一种类型。然后,问题就变成了根本没有满足所有这些约束的默认数字类型(事实上,我认为通常是数字类型),因此 GHC 告诉你 "possible instances" 它会尝试。解决办法是加fromIntegral,放宽约束:

factorPairs:: Integral a => a -> [(a, a)]
factorPairs n = map(\x -> (x, div n x)) [y | y <- [1..(ceiling $ sqrt $ fromIntegral n)], n `rem` y == 0]

消除类型错误的另一种方法是消除 sqrt 的使用。由于 Haskell 是惰性的,您可以简单地迭代 [1..n],当除数大于商时停止。

factorPairs :: Integral a => a -> [(a, a)]
factorPairs n = takeWhile (uncurry (>=)) [ (n `div` d, d) | d <- [1..n], n `mod` d == 0]

uncurry (>=) 只是一种奇特的写法 \(q, d) -> q >= d.

如果您以单子形式编写此代码,则可以使用 divMod 通过单个函数 all 获取商和余数。

factorPairs n = takeWhile (uncurry (>=)) $ do
                d <- [1..n]
                let (q, r) = n `divMod` d
                guard $ r == 0
                return (q, d)