我正在教自己Haskell,并且使用列表理解。我写了这个列表理解:
[ c | a <- [1..3], b <- [1..4], let c = hyp a b, c == round c]
我希望它会产生值列表c,其中c是一个整数(c == round c),它只有5,但它不能编译。再玩一些,我发现我无法在列表理解中嵌入任何功能,我确定有办法,我只是不知道如何。
这是错误代码:
<interactive>:158:1: error:
• Ambiguous type variable ‘t0’ arising from a use of ‘it’
prevents the constraint ‘(Floating t0)’ from being solved.
Probable fix: use a type annotation to specify what ‘t0’ 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
谢谢!
答案 0 :(得分:2)
首先,在这样的问题中包含必要的定义。
hyp :: Floating a => a -> a -> a
hyp a b = sqrt $ a^2 + b^2
现在。 您可以在列表推导中“嵌入”功能。显然你只是选择了一些不幸的人! round
具有以下类型:
GHCi, version 7.10.2: http://www.haskell.org/ghc/ :? for help
Prelude> :t round
round :: (Integral b, RealFrac a) => a -> b
因此,要使round c == c
有意义,您需要一个同时属于Integral
和RealFrac
的数字类型。换句话说,这种类型将包含分数,但其所有元素都是整数†。好吧,你不能吃蛋糕而且也吃它!
如果你真的写了一些类型的签名,这个问题会更加明显,就像在Haskell中经常出现的那样。虽然这样愚弄,但选择一些简单的示例类型通常会有所帮助。也许最合理的事情似乎是:
Prelude> [ c | a <- [1..3], b <- [1..4], let c = hyp a b, c == round c] :: [Integer]
<interactive>:6:41:
No instance for (Floating Integer) arising from a use of ‘hyp’
In the expression: hyp a b
In an equation for ‘c’: c = hyp a b
In the expression:
[c |
a <- [1 .. 3], b <- [1 .. 4], let c = hyp a b, c == round c] ::
[Integer]
好的,所以Integer
不起作用,因为你试图用hyp
中的那些平方根进行真正的算术运算。 Integer
无法做到这一点,您需要Floating
类型的Double
类型。我们试试吧:
Prelude> [ c | a <- [1..3], b <- [1..4], let c = hyp a b, c == round c] :: [Double]
<interactive>:8:55:
No instance for (Integral Double) arising from a use of ‘round’
In the second argument of ‘(==)’, namely ‘round c’
In the expression: c == round c
In a stmt of a list comprehension: c == round c
好的,这是因为正如我所说,round
总是给出整数型结果。但是,您始终可以将此类整数类型再次转换为Double
:
Prelude> [ c | a <- [1..3], b <- [1..4], let c = hyp a b, c == fromIntegral (round c)] :: [Double]
[5.0]
请注意,这不是一个很好的解决方案:如果你已经检查过这些元素是不是完整的,你真的不希望结果是浮点数!在这种情况下,我建议不要只评估hyp
。更好地利用这种理解:
Prelude> [ c | a <- [1..3], b <- [1..4], let c² = a^2 + b^2; c = round . sqrt $ fromIntegral c², c^2==c²] :: [Integer]
[5]
此版本的一个重要论点是它在Integer
中进行比较,而不是在Double
中进行比较。浮点平等比较是你最好远离完全的,如果你可以帮助它;在这种情况下,它的主要是无害的,因为有趣的是整数子集,实际上可以完全表示(不像0.1
这样的小数部分)。你仍然可以通过这种方式得到错误的结果:特别是,对于足够大的浮点数,c == fromInteger (round c)
总是为真,因为在某个阈值之上,所有值都是积分的。< / p>
Prelude> [ c | a <- take 4 [1000000000000..], b <- take 5 [1000000000000..], let c = hyp a b, c == fromIntegral (round c)] :: [Float]
[1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12,1.4142135e12]
但这些都不是正确的积分hypothenuses,正如您在Integer
中进行比较的版本所见:
Prelude> [ c | a <- take 4 [1000000000000..], b <- take 5 [1000000000000..], let c² = a^2 + b^2; c = round . sqrt $ fromIntegral c², c^2==c²] :: [Integer]
[]
严格来说,这个改进版本也不安全 - 它不会给出误报,但可能找不到实际的毕达哥拉斯三元组,因为有损浮点步骤已经可以破坏平等。要真正做到这一点,你需要一个全集的
intSqrt :: Integral a => a -> Maybe a
通过将float sqrt
作为整数算术中几轮伪Newton-Raphson的起始值,这可能非常有效。
† 原则上,round
功能也可以有更宽松的签名。
round' :: (Num b, RealFrac a) => a -> b
round' = fromInteger . round
使用该版本,您的原始代码将起作用。