簡體   English   中英

如何處理Haskell中的類型

[英]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)。 您還可以使用modrem查找余數,例如典型語言中的% (對於負數,它們是不同的)

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) divmod可以精確工作,因此沒有舍入錯誤的危險。

哈斯克爾永遠, 永遠不會隱式類型轉換。 對於大多數程序員來說,這似乎有些固執,但這實際上是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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM