了解使用 sqrt 时与类型相关的 Haskell 错误
Understanding type-related Haskell errors when using sqrt
我已经阅读 Learn You a Haskell 几天了(第 1-6 章),有几件事,即使是新手,我也不清楚。
例如,我一直在尝试实现一个脚本来解决 this classic problem 寻找整数的最大质因数的问题。
我很确定我的方法,
- 检查数字是否为正方形,如果是,return检查它的平方根,
- 否则从平方根向下搜索除数,只扫描奇数,
可能不正确,显然是错误的,正如答案和评论中所指出的(我希望我会我自己已经意识到这一点,如果我能够编写可编译的代码的话),但至少这是一个练习 Haskell 语法的机会,而不仅仅是。
唯一的注释行是我遇到问题的那一行。 "strange" 对我来说是什么,错误是关于那之后的两行,而如果我将 lamda 更改为不使用 x
和 e
的东西,一切正常(除了结果是错误的,很明显)。
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
我的理解是 e
和 x
推导的类型有问题,但是考虑到我目前的水平,这个错误不是很有用:
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
问题就在这里:它的类型要求输入值的类型同时是 Integral
和 Floating
和 RealFrac
类型。
为什么?因为相同的 x
被用作 mod
和 sqrt
的输入(并且,在 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
)。
我已经阅读 Learn You a Haskell 几天了(第 1-6 章),有几件事,即使是新手,我也不清楚。
例如,我一直在尝试实现一个脚本来解决 this classic problem 寻找整数的最大质因数的问题。
我很确定我的方法,
- 检查数字是否为正方形,如果是,return检查它的平方根,
- 否则从平方根向下搜索除数,只扫描奇数,
可能不正确,显然是错误的,正如答案和评论中所指出的(我希望我会我自己已经意识到这一点,如果我能够编写可编译的代码的话),但至少这是一个练习 Haskell 语法的机会,而不仅仅是。
唯一的注释行是我遇到问题的那一行。 "strange" 对我来说是什么,错误是关于那之后的两行,而如果我将 lamda 更改为不使用 x
和 e
的东西,一切正常(除了结果是错误的,很明显)。
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
我的理解是 e
和 x
推导的类型有问题,但是考虑到我目前的水平,这个错误不是很有用:
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
问题就在这里:它的类型要求输入值的类型同时是 Integral
和 Floating
和 RealFrac
类型。
为什么?因为相同的 x
被用作 mod
和 sqrt
的输入(并且,在 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
)。