2015-08-22 41 views
8
Ben şu tip Enum bir örneğini yazmaya çalışıyorum

:Haskell numaralandırma

-- Type declarations: 

-- Octave 
data Octave = 
    O1 | O2 | O3 
    deriving (Show, Read, Eq, Ord, Bounded, Enum) 

-- Note 
data Note = 
    A | B | C | D | E | F 
    deriving (Show, Read, Eq, Ord, Bounded, Enum) 

-- Pitch 
data Pitch = Pitch Octave Note 
    deriving (Show, Eq, Ord) 

-- Why doesn't this work? 
instance Enum Pitch where 
    fromEnum (Pitch o n) = (fromEnum o)*6 + (fromEnum n) 
    toEnum x = (Pitch o n) 
     where 
      o = toEnum (x `div` 6) 
      n = toEnum (x `mod` 6) 

Bu için çalışıyor:

[(Pitch O1 A) .. (Pitch O3 F)] 

Ama başarısız:

[(Pitch O1 A) .. ] 

ile hata:

*** Exception: toEnum{Octave}: tag (3) is outside of enumeration's range (0,2) 

Hatayı anlıyorum. Sorularım şunlardır: Bu numaralandırma için Enum örneğini doğru şekilde yazmak nasıl? Mümkün mü? En önemlisi: Bu iyi bir uygulama mı?

enumFrom x = map toEnum [fromEnum x ..] 

geçersiz kılma sizin örneği tarafından doğru olanı yapmak için:

cevap

9

Senin sorunun Pitch örtülü sınırlanmış olmasıdır. Bunun succ s Pitch O3 F kadar üretilen değerleri ing succ tutar ve darbeler

enumFrom (Pitch O1 A) 

kod

[Pitch O1 A ..] 

desugars. Orada durması gerektiğini nasıl bilebilirdi?

From the Prelude documentation: Böylece

For any type that is an instance of class Bounded as well as Enum , the following should hold:

  • enumFrom and enumFromThen should be defined with an implicit bound, thus:

    enumFrom  x = enumFromTo  x maxBound 
    enumFromThen x y = enumFromThenTo x y bound 
        where 
        bound | fromEnum y >= fromEnum x = maxBound 
          | otherwise    = minBound 
    

, bu sorunu çözmeye yönelik, sadece

instance Bounded Pitch where 
    minBound = Pitch minBound minBound 
    maxBound = Pitch maxBound maxBound 

ekleyin ve sonra belgelerinden kodu ekleyin:

instance Enum Pitch where 
    -- ... 
    enumFrom  x = enumFromTo  x maxBound 
    enumFromThen x y = enumFromThenTo x y bound 
    where 
     bound | fromEnum y >= fromEnum x = maxBound 
      | otherwise    = minBound 

ve şimdi [Pitch O1 A ..] sonunda duracak:

λ> [Pitch O1 A ..] 
[Pitch O1 A,Pitch O1 B,Pitch O1 C,Pitch O1 D,Pitch O1 E,Pitch O1 F,Pitch O2 A,Pitch O2 B,Pitch O2 C,Pitch O2 D,Pitch O2 E,Pitch O2 F,Pitch O3 A,Pitch O3 B,Pitch O3 C,Pitch O3 D,Pitch O3 E,Pitch O3 F] 

Yan not: divMod tek bir çağrı desen eşleştirme ile div ve mod ayrı çağrıları yerini alabilir: x `divMod` y == (x `div` y, x `mod` y). (Bunlar gibi kesinlikle pozitif sayılar için, quotRem'un daha iyi bir seçenek olduğunu duyduğuma inanıyorum; quot ve rem, ve mod gibidir, ancak farklı oturumla ilgili davranışlara sahiptir.) Ayrıca, 6 Yanlışlıkla yanlış numarayı almaktan kaçınmak için 1 + (fromEnum (maxBound :: Note)).

5

[x ..] hangi prelude provides the following default implementation için enumFrom x yöntemde, içine şekeri alınmış olup. Benzer nedenlerle enumFromThen'u geçersiz kılmak isteyebilirsiniz. yani, bir az ve en büyük elemanı var - - ama kodunuzda bu sınırlılık yansıtmamaktadır

+0

Teşekkürler! 'enumFrom x = harita toEnum [fromEnum x ..17] 'Bu iyi bir stil mi? – dos