了解使用 sqrt 时与类型相关的 Haskell 错误

Understanding type-related Haskell errors when using sqrt

我已经阅读 Learn You a Haskell 几天了(第 1-6 章),有几件事,即使是新手,我也不清楚。

例如,我一直在尝试实现一个脚本来解决 this classic problem 寻找整数的最大质因数的问题。

我很确定我的方法,

可能不正确,显然是错误的,正如答案和评论中所指出的(我希望我会我自己已经意识到这一点,如果我能够编写可编译的代码的话),但至少这是一个练习 Haskell 语法的机会,而不仅仅是。

唯一的注释行是我遇到问题的那一行。 "strange" 对我来说是什么,错误是关于那之后的两行,而如果我将 lamda 更改为不使用 xe 的东西,一切正常(除了结果是错误的,很明显)。

largestprime x
  | isSquare = srx
  | otherwise = head $ filter (\e -> x `mod` e == 0) lst --Lambda
    where intsqrt = floor . sqrt
          isSquare = sqrt x == fromInteger(intsqrt x)
          srx = intsqrt x
          lst = [i,i-2..]
            where i = if odd srx then srx else srx - 1

我的理解是 ex 推导的类型有问题,但是考虑到我目前的水平,这个错误不是很有用:

Main.hs:59:21: error:
    • No instance for (RealFrac Integer) arising from a use of ‘floor’
    • In the first argument of ‘(.)’, namely ‘floor’
      In the expression: floor . sqrt
      In an equation for ‘intsqrt’: intsqrt = floor . sqrt
   |
59 |     where intsqrt = floor . sqrt
   |                     ^^^^^

Main.hs:59:29: error:
    • No instance for (Floating Integer) arising from a use of ‘sqrt’
    • In the second argument of ‘(.)’, namely ‘sqrt’
      In the expression: floor . sqrt
      In an equation for ‘intsqrt’: intsqrt = floor . sqrt
   |
59 |     where intsqrt = floor . sqrt
   |                             ^^^^

Main.hs:60:22: error:
    • No instance for (Floating Integer) arising from a use of ‘sqrt’
    • In the first argument of ‘(==)’, namely ‘sqrt x’
      In the expression: sqrt x == fromInteger (intsqrt x)
      In an equation for ‘isSquare’:
          isSquare = sqrt x == fromInteger (intsqrt x)
   |
60 |           isSquare = sqrt x == fromInteger(intsqrt x)
   |                      ^^^^^^
Failed, no modules loaded.

关于e,它是lst的一个元素,它是一个列表,其元素与srx的类型相同,这是[=输出的类型20=],它基于 :t floor 似乎是 Integral。所以也许我只需要适当地定义 largestprime 的类型?

我已经看到一些关于该主题的问题,例如 this one or this one。但是我现在有点困惑。

除了获得帮助以解决问题外,如果能收到一些关于如何阅读这些错误的建议也很好,目前这些错误对我来说接近阿拉伯语。

在某些地方,您似乎对使用的是 Integral 类型还是 RealFrac 类型感到有些困惑。确保您不会混淆自己的一个好方法是使用类型注释。

首先,您正在创建的函数的输入和输出是什么,largestprime? IMO,它应该是两端的整数类型,因为如果你有非整数,谈论质因数是没有意义的。

largestprime :: Integral a => a -> a

那么,接下来我们要求输入的平方根。不幸的是,sqrt 不属于 Integral,而是属于 Floating(在 GHCi 中只是 运行 :i sqrt)。我们可以做的是使用 fromIntegral 函数,它具有以下类型签名:

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

所以为了找到整数的平方根,我们必须使用sqrt . fromIntegral。要检查数字是否为正方形,我们可以这样做

isSquare = let y = fromIntegral x in sqrt y == fromIntegral (floor $ sqrt y)

isSquare = let y = fromIntegral x in ceiling (sqrt y) == floor (sqrt y)

到目前为止,我们已经弄清楚了 "is a perfect square" 案例并开始工作。接下来是剩下的。

我们要检查可分割性的候选列表是 [i, i-2 ..],因为 i 正如您定义的那样。因此,按照您的代码,我们首先过滤掉 x:

filter (\n -> x `mod` n == 0) [i, i-2, ..]

我们想要第一个符合我们条件(即未过滤)的数字。因此,就像您所做的那样,我们采用 head:

head $ filter (\n -> x `mod` n == 0) [i, i-2, ..]

整个函数是:

largestprime :: Integral a => a -> a
largestprime x  | isSquare  = root
                | otherwise = head $ filter (\n -> x `mod` n == 0) [i, i-2 ..]
    where   isSquare = let y = fromIntegral x in ceiling (sqrt y) == floor (sqrt y)
            root     = floor $ sqrt $ fromIntegral x
            i        = if odd root then root else root - 1

现在我们有了一个编译成功的函数。我并没有在这个答案中真正涵盖这一点,但解决 GHC 错误的一个技巧是从底部开始——总是首先修复最后一个错误。在您的情况下,您会立即看到 sqrt x 抱怨 x 不是 Floating 类型。

我只是认为还值得一提的是,您的算法并没有按照您的想法进行。当然,它会 return 它的因子之一,但它不一定是质数(largestprime 36 给出 6,这不是质数)。

定义函数后,立即检查其类型:

> :t largestprime
largestprime :: (RealFrac c, Integral c, Floating c) => c -> c

问题就在这里:它的类型要求输入值的类型同时是 IntegralFloatingRealFrac 类型。

为什么?因为相同的 x 被用作 modsqrt 的输入(并且,在 intsqrt 中,floor):

> :t mod
mod   ::  Integral a              => a -> a -> a

> :t sqrt
sqrt  ::  Floating a              => a -> a

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

> :t floor . sqrt
floor . sqrt :: (RealFrac a, 
          Integral b, Floating a) => a ->      b

为什么编译?因为从理论上讲,您可以在其他一些模块中定义一个类型,该类型是约束中所有类型类的一个实例,并与它一起使用此函数。

错误 No instance for (RealFrac Integer)No instance for (Floating Integer) 仅表示您没有这样做(您的 Integral 已默认为 Integer)。