[英]Haskell instance function
我正在解決一本書中的一些練習,但現在我遇到了一些困難:在這個練習中,我將實現 Card 作為 class Ord 的一個實例。 但我不知道我該如何實施它,所以我將不勝感激任何幫助。
到目前為止,我的代碼如下所示:
data Suit = Diamond | Club | Spade | Heart
data Rank = Seven | Eight | Nine | Ten | Jack | Queen | King | Ace
data Card = Card Suit Rank
instance Ord Card where
....
現在我不知道究竟如何實現這一點,我非常想了解它。 提前感謝您的解釋。
我無法想象練習實際上要您手動編寫必要的實例。 特別是對於Suit
和Rank
,編寫它們會非常乏味(並且容易出錯)。
相反,您可能希望使用deriving
關鍵字自動派生必要的實例。 如果你寫:
data Suit = Diamond | Heart | Spade | Club
deriving (Eq, Ord)
這會自動派生Eq
和Ord
實例。 ( Ord
實例需要Eq
實例。)派生的Eq
實例是不言自明的,派生的Ord
實例使用構造函數在聲明中出現的順序,因此Diamond
最小, Club
最大。 如果您想要橋接訂單,請使用:
data Suit = Club | Diamond | Heart | Spade
deriving (Eq, Ord)
反而。 相似地,
data Rank = Seven | Eight | Nine | Ten | Jack | Queen | King | Ace
deriving (Eq, Ord)
從Seven
(最小)到Ace
(最大)排序。 然后,如果你寫:
data Card = Card Rank Suit
deriving (Eq, Ord)
派生實例根據第一個字段Rank
對Cards
進行排序,並由Suit
打破聯系。 如果你寫道:
data Card = Card Suit Rank
deriving (Eq, Ord)
它會改為Suit
排序,然后按Rank
。
一些代碼來說明:
data Suit = Diamond | Heart | Spade | Club
deriving (Eq, Ord)
data Rank = Seven | Eight | Nine | Ten | Jack | Queen | King | Ace
deriving (Eq, Ord)
data Card = Card Rank Suit
deriving (Eq, Ord)
main = do
let heart8 = Card Eight Heart
spade8 = Card Eight Spade
diamond7 = Card Seven Diamond
print $ heart8 == heart8 -- True
print $ heart8 == spade8 -- False
print $ heart8 < spade8 -- True
print $ diamond7 < spade8 -- True
我們可以向 GHCi 詢問有關Ord
的信息。
:info Ord
這顯示了 class 定義,后跟實例列表。
type Ord :: * -> Constraint
class Eq a => Ord a where
compare :: a -> a -> Ordering
(<) :: a -> a -> Bool
(<=) :: a -> a -> Bool
(>) :: a -> a -> Bool
(>=) :: a -> a -> Bool
max :: a -> a -> a
min :: a -> a -> a
{-# MINIMAL compare | (<=) #-}
-- Defined in ‘GHC.Classes’
它的超類是Eq
, :info Eq
告訴我們:
type Eq :: * -> Constraint
class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
{-# MINIMAL (==) | (/=) #-}
-- Defined in ‘GHC.Classes’
所以為了實現Card
的Ord
,我們需要兩件事:
instance Ord Card
,具有compare
或(<=)
的定義(==)
或(/=)
定義的instance Eq Card
由於這些實例編寫起來非常機械,通常我們要求編譯器自動派生它們:
data Suit = Diamond | Heart | Spade | Club
deriving (Eq, Ord)
data Rank = Seven | Eight | Nine | Ten | Jack | Queen | King | Ace
deriving (Eq, Ord)
data Card = Card Suit Rank
deriving (Eq, Ord)
如果您將-ddump-deriv
標志傳遞給 GHC,或在 GHCi 中使用:set -ddump-deriv
,它將打印出它為這些實例生成的代碼。
然而,任務是了解如何手動實現這些實例,所以讓我們 go 一步一步地完成它。
我們將從Eq
開始。 對於Suit
和Rank
,我們需要指定每個構造函數都等於其自身(也稱為自反性),而所有其他構造函數都不相等。
instance Eq Suit where
Diamond == Diamond = True
Heart == Heart = True
Spade == Spade = True
Club == Club = True
_ == _ = False
instance Eq Rank where
Seven == Seven = True
Eight == Eight = True
Nine == Nine = True
Ten == Ten = True
Jack == Jack = True
Queen == Queen = True
King == King = True
Ace == Ace = True
_ == _ = False
有了這個,我們可以通過對Card suit rank
形式的值進行模式匹配,以類似的方式為Card
定義==
。
instance Eq Card where
-- Two cards are equal if…
Card suit1 rank1 == Card suit2 rank2
-- …their suits are equal,
-- and their ranks are equal.
= …
有兩種傳統的方法來定義主體。 一個是字面拼寫: suit1 == suit2 && rank1 == rank2
。 另一種是使用元組: (suit1, rank1) == (suit2, rank2)
。
同樣,要定義Ord
,我們可以從枚舉Suit
和Rank
的實例開始。 關於如何指定這些,我們有兩種選擇: (<=)
或compare
。 直接的選擇是只列出可能的案例表,我們可以縮寫案例:
instance Ord Suit where
Diamond <= _ = True
Heart <= Diamond = False
Heart <= _ = True
Spade <= Diamond = False
Spade <= Heart = False
Spade <= _ = True
Club <= Club = True
Club <= _ = False
instance Ord Rank where
…
對於Card
,我們現在只需要遵循與Eq
相同的基本形式。 由於我們為Suit
和Rank
定義了(<=)
,請注意,我們會自動獲得其他比較函數的默認實現,例如這些類型的compare
和(<)
。
instance Ord Card where
-- Two cards are in order if…
Card suit1 rank1 <= Card suit2 rank2
-- …one rank is less than the other,
-- or their ranks are equal and suits are in order.
= …
同樣,您可以使用(<)
、 (||)
、 (==)
、 (&&)
、 (<=)
逐字翻譯此評論。
嘗試基於compare
而不是(<=)
來實現這些實例。 提示:使用Data.Ord
中的comparing
和instance Monoid Ordering
。
將deriving (Enum)
添加到Rank
和Suit
嘗試使用fromEnum
來簡化它們的Eq
和Ord
定義。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.