2016-11-08 92 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 

與該版本,原來的代碼會工作。