我是一个OOP /势在必行的程序员,正在尝试学习haskell,我正在开发一种卡片游戏作为练习。
当时,我试图定义库牌组:
data Suit = Hearts
| Diamonds
| Spades
| Clubs
deriving(Show, Eq)
type CardValue = Int
data Card = Ace {cSuit :: Suit}
| Two {cSuit :: Suit}
| Three {cSuit :: Suit}
| Four {cSuit :: Suit}
| Five {cSuit :: Suit}
| Six {cSuit :: Suit}
| Seven {cSuit :: Suit}
| Eight {cSuit :: Suit}
| Nine {cSuit :: Suit}
| Ten {cSuit :: Suit}
| Jack {cSuit :: Suit}
| Queen {cSuit :: Suit}
| King {cSuit :: Suit}
deriving(Show, Eq)
type Hand = [Card]
type Deck = [Card]
cardValue :: Card -> Int
cardValue card =
| card.ValueConstructorName == Ace = 11
| card.ValueConstructorName == Seven = 10
| otherwise = 0
1)card.ValueConstructorName是幻想字段/方法/其他。有没有办法用卫兵来实现这个目标?
2)我是否真的需要在值构造函数的每个替代中键入“{cSuit :: Suit}”?
3)这种抽象卡牌是否合理?我的意思是,使用嵌套类型 谢谢你
答案 0 :(得分:8)
1)这称为模式匹配。
cardValue (Ace suit) = 11
cardValue (Seven suit) = 10
cardValue ....
2)不。这是代表卡片的一种非常繁琐的方式。这里有一个非常明显的模式;在haskell中,每当你看到一个非常重复的模式时,你可以保证有一些方法来抽象出来以使事情变得更好。请尝试以下方法:
data Value = Ace | Two | Three | .... | King deriving (Show, Eq, Enum)
关于此问题的最佳部分是,您可以为此类数据类型派生Enum
,Enum
定义一个名为fromEnum
的函数,该函数会将CardValue
转换为Int
。正如您所料,第0个构造函数将为0等,因此如果ace == 1则为:
cardValue1 :: Value -> Int
cardValue1 x = 1 + fromEnum x
然后你定义一张卡片:
data Card = Card Value Suit
或者如果你真的想要记录:
data Card = Card {valueOfCard :: Value, suitOfCard :: Suit}
但通常使用包含2个字段的数据类型的记录是没有意义的。甚至:
type Card = (Value, Suit)
然后:
cardValue :: Card -> Int
cardValue (Card v _) = cardValue1 v
3)'几乎所有'类型都是嵌套的。除了嵌套类型之外,您没有其他选择。有一些原始类型,你几乎肯定都看不到,其他类型只是这些原语的某种组合。
答案 1 :(得分:0)
您需要的是模式匹配:
cardValue :: Card -> Int
cardValue (Ace suit) = ...
cardValue (Two suit) = ...
cardValue (Three suit) = ...
...
你可以这个或一个案例陈述
cardValue :: Card -> Int
cardValue card = case card of
Ace suit -> ...
Two suit -> ...
...
至于这是否是一个好模型,我会建议反对它。 user2407038显示的方式是更好的选择,特别是与Enum
和Bounded
类型组合时。