2016-11-08 52 views
1

我在教自己Haskell,并玩列表解析。我写这个list解析:Haskell:列表解析中的函数

[ c | a <- [1..3], b <- [1..4], let c = hyp a b, c == round c] 

我希望其中c为(C ==轮C),这将是只有5它将产生值C的列表,但它不编译。玩了更多,我发现我不能真正将任何函数嵌入到列表理解中,我确信有一种方法,我只是不知道如何。

这里的错误代码:

<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

我不知道你的'hyp'是什么样的,但是你可以在理解的最后一个语句 –

+0

上试试'c == fromIntegral(round c)'这就是它,非常感谢!在继续前进之前,我只需要研究fromIntegral的角色。 –

回答

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] 

好了,Integerhyp工作,因为你正在尝试做真正的算法,这些平方根。这不可能与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)总是为真,因为超过一定的阈值,所有的值都是积分。

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 

这可以通过采取浮动sqrt作为在整数运算几个回合伪Newton-Raphson的初始值可能做得相当有效。


原则上,round功能还可以有更宽松的签名。

round' :: (Num b, RealFrac a) => a -> b 
round' = fromInteger . round 

与该版本,原来的代码会工作。