我正在解決一本書中的一些練習,但現在我遇到了一些困難:在這個練習中,我將實作 Card 作為類 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
....
現在我不知道究竟如何實作這一點,我非常想了解它。提前感謝您的解釋。
uj5u.com熱心網友回復:
我們可以向 GHCi 詢問有關Ord.
:info Ord
這顯示了類定義,后面是實體串列。
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’
所以為了實作Ordfor Card,我們需要兩件事:
- 一個
instance Ord Card,定義為compareor(<=) instance Eq Card定義為(==)or(/=)
由于這些實體撰寫起來非常機械,通常我們要求編譯器自動派生它們:
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,或:set -ddump-deriv在 GHCi 中使用,它將列印出它為這些實體生成的代碼。
然而,任務是理解如何手動實作這些實體,所以讓我們一步一步來。
我們將從Eq. 對于Suitand 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
有了這個,我們可以通過對 form 的值進行模式匹配,以類似的方式定義==for 。CardCard suit rank
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。由于我們為 and 定義了(<=),請注意我們會自動獲得其他比較函式的默認實作,例如and用于那些型別。SuitRankcompare(<)
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而不是實作這些實體(<=)。提示:使用comparing和instance Monoid Ordering從Data.Ord。
添加deriving (Enum)到Rank和Suit——嘗試使用fromEnum來簡化它們的Eq定義Ord。
uj5u.com熱心網友回復:
我無法想象練習實際上要您手動撰寫必要的實體。特別是對于Suit和Rank,它們的撰寫起來會非常乏味(而且容易出錯)。
相反,您可能希望使用deriving關鍵字來自動派生必要的實體。如果你寫:
data Suit = Diamond | Heart | Spade | Club
deriving (Eq, Ord)
這會自動派生Eq和Ord實體。(Eq實體需要Ord實體。) 派生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)
派生實體Cards根據第一個欄位排序Rank,并由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
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/537531.html
標籤:哈斯克尔卡片
下一篇:Haskell任務實體一般
