[英]How to deal with types in Haskell
我已經開始學習Haskell,並且一直在閱讀“學習Haskell帶來的好處”。 我閱讀了“模塊”一章的一半內容。 一位朋友向我展示了代碼戰,我決定將我學到的一些東西進行測試。
我正在嘗試制作一個函數,該函數返回一個布爾值,該布爾值是否為給定的Integral是4的冪。這是代碼。
module PowerOfFour where
isWhole n = fromIntegral (round n) == n
isPowerOf4 :: Integral n => n -> Bool
isPowerOf4 4 = True
isPowerOf4 x = if x < 4 then
False
else
if isWhole (x / 4) then
isPowerOf4 (truncate (x / 4))
else
False
這是我收到的錯誤消息。
/tmp/haskell11524-8-kke52v/PowerOfFour.hs:10:12:
Could not deduce (RealFrac n) arising from a use of `isWhole'
from the context (Integral n)
bound by the type signature for
isPowerOf4 :: Integral n => n -> Bool
at /tmp/haskell11524-8-kke52v/PowerOfFour.hs:5:15-37
Possible fix:
add (RealFrac n) to the context of
the type signature for isPowerOf4 :: Integral n => n -> Bool
In the expression: isWhole (x / 4)
In the expression:
if isWhole (x / 4) then isPowerOf4 (truncate (x / 4)) else False
In the expression:
if x < 4 then
False
else
if isWhole (x / 4) then isPowerOf4 (truncate (x / 4)) else False
/tmp/haskell11524-8-kke52v/PowerOfFour.hs:10:23:
Could not deduce (Fractional n) arising from a use of `/'
from the context (Integral n)
bound by the type signature for
isPowerOf4 :: Integral n => n -> Bool
at /tmp/haskell11524-8-kke52v/PowerOfFour.hs:5:15-37
Possible fix:
add (Fractional n) to the context of
the type signature for isPowerOf4 :: Integral n => n -> Bool
In the first argument of `isWhole', namely `(x / 4)'
In the expression: isWhole (x / 4)
In the expression:
if isWhole (x / 4) then isPowerOf4 (truncate (x / 4)) else False
我究竟做錯了什么?
我在isWhole中使用了fromIntegral來修復類似的錯誤,並且isWhole現在可以單獨正常使用了。 但是,我似乎無法擺脫isPowerOf4中的這些錯誤。 我已經嘗試了GHC提供的可能的修復程序,但是我可能做得不好。
我寧願保留isPowerOf4函數的類型簽名,因為這是代碼戰提供的,所以我猜想這是必需的。
在Haskell中, /
運算符用於分數類型,而不是整數類型。 對於整數類型,應使用div
(您可以通過將其括在反引號中來使用infix)。 您還可以使用mod
或rem
查找余數,例如典型語言中的%
(對於負數,它們是不同的)
isPowerOf4 :: Integral n => n -> Bool
isPowerOf4 4 = True
isPowerOf4 x = if x < 4
then False
else
if x `mod` 4 == 0
then isPowerOf4 (x `div` 4)
else False
正如@leftaroundabout所解釋的/
是浮點除法,結果可能是舍入誤差的對象。 因此,不能保證您的isWhole
函數在所有情況下都能正常工作。
更好的方法是使用@Jubobs建議的整數除法和取模。
div
是整數除法,它為您提供除法結果,而無余數。 這與您對truncate (x / 4)
期望相同。 應將其重寫為div x 4
,因此它不依賴浮點數。
對於isWhole
您可以使用模,這是整數除法的其余部分。 (例如mod 5 4 == 1
)如果為零,則沒有余數,結果為整數。 可以使用mod x 4 == 0
代替isWhole (x / 4)
。 div
和mod
可以精確工作,因此沒有舍入錯誤的危險。
哈斯克爾永遠, 永遠不會隱式類型轉換。 對於大多數程序員來說,這似乎有些固執,但這實際上是Haskell的類型系統如此出色的原因之一。 完全回避的轉換使許多事情變得更加簡單,這是完全雙向類型推斷的前提。
在您的情況下,問題出在x / 4
。 除法,您似乎希望結果可能不是整數。 但是它必須是整數,只要它具有整數類型1即可 ! 因此,您需要先明確轉換為分數類型2 。 實際上,最簡單的方法是您已經發現的fromIntegral
函數:
if isWhole $ fromIntegral x / 4 then ...
明確說明:這就像if isWhole ( (fromIntegral x)/4 ) then
解析一樣。 在這一點上,您可能會懷疑:如果我只是說過Haskell從來沒有隱式地轉換東西,那為什么用整數4
來除法還可以呢? 答案是,Haskell沒有整數文字! 它只有各種數字文字 。 整數文字沒有任何特定的類型,它是多態的(即,它從上下文中請求此處應使用哪種類型 ,並且如果該類型位於Num
類中,則該行為類似於該類型)。
1 C通過使整數除以trutru結果來“解決”該問題……這導致了很多問題,可能比隱式轉換要多。
2 “某些分數類型”在Haskell中表示默認為Double
。 這種類型非常快,但也不是完全沒有問題,因為浮點數本質上是不精確的。 我現在不確定這是否會影響您的應用程序; 由於==
比較,它很可能是。 基本上,您需要指定isWhole
的類型來防止這種情況,例如isWhole :: Rational -> Bool
。 Rational
類型將非整數表示為精確分數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.