我的卡片类型为西装和等级,
data Suit = A|B deriving (Show, Eq)
data Rank = 1|2 deriving (Show, Eq)
data Card = Card Suit Rank deriving (Show, Eq)
数据排名函数似乎是错误的,因为如果我的卡片是A1 | B1 | A2 | B2,则Int不能是类型构造函数以及如何创建正确的函数
谢谢
答案 0 :(得分:1)
看起来好像是这样:
data Suit = A | B
仅将一种类型Suit
定义为任意对象的集合/集合。但是,实际上,它定义了三件事:类型Suit
和两个构造函数A
和B
用于创建该类型的值。
如果定义:
data Rank = 1 | 2
实际上是可行的,它不会将Rank
定义为数字1
和2
的集合,而是会重新定义数字{{ 1}}和1
作为新类型2
的构造函数/值,那么您将无法再将它们用作常规数字。 (例如,表达式Rank
现在将是类型错误,因为n + 1
需要一个数字,而(+)
将被重新定义为1
)。
幸运的是,Haskell不会接受数字作为构造函数名称-它们必须是以大写字母开头的有效标识符(或以冒号开头的运算符)。
因此,有两种常用的方法来定义Rank
之类的类型,该类型旨在表示数字的某些子集。如注释中所述,第一个方法是像已经定义的那样进行定义,但是通过在数字前加上大写字母来将其更改为有效的标识符:
Rank
这样做的好处是可以保证只能表示有效等级。在此,仅允许等级1和2。如果有人试图将data Rank = R1 | R2
写在某个地方,那将是行不通的,因为尚未定义该构造函数。最大的缺点是,这很快变得难以控制。如果这些是扑克牌,则定义为:
R3
和一个为点牌分配点值给卡片的函数看起来像:
data Rank = R1 | R2 | R3 | R4 | R5 | R6 | R7 | R8 | R9 | R10 | R11 | R12 | R13
(在实际代码中,您将使用更高级的Haskell功能(例如派生的points :: Rank -> Int
points R1 = 10 -- Ace worth 10
points R2 = 2
points R3 = 3
...
points R9 = 9
points R10 = 10 -- 10 and face cards all worth 10
points R11 = 10
points R12 = 10
points R13 = 10
实例来处理此问题。)
第二种方法是根据现有数字类型定义排名:
Enum
这定义了两件事,一个名为data Rank = Rank Int -- actually, `data` would probably be `newtype`
的类型,另一个也叫Rank
的构造函数。 (没关系,因为类型和构造函数位于不同的名称空间中。)
在此定义中,不是将Rank
定义为由显式构造函数给出的离散值集(每个值一个构造器),此定义必不可少,使类型Rank
成为Rank
用构造函数Int
“标记”。
此方法的缺点是现在可以创建无效的等级(因为有人可以写Rank
)。优点是通常更容易使用。例如,您可以从等级中提取整数,因此Rank 14
可以定义为:
points
请注意,使用以下定义集:
points :: Rank -> Int
points (Rank 1) = 10 -- Ace is worth 10
points (Rank r) | r >= 10 = 10 -- 10 and face are worth 10
| otherwise = r -- rest are worth their rank
您将使用data Suit = A | B deriving (Show, Eq)
newtype Rank = Rank Int deriving (Show, Eq)
data Card = Card Suit Rank deriving (Show, Eq)
之类的表达式为您的“ A1”卡构造Card
值。
实际上有第三种方法。有些人可能会完全跳过定义Card A (Rank 1)
类型的定义,而要么写:
Rank
或使用类型别名编写等效代码:
data Suit = A | B deriving (Show, Eq)
data Card = Card Suit Int deriving (Show, Eq)
请注意,这里的类型别名实际上仅用于文档说明。 data Suit = A | B deriving (Show, Eq)
type Rank = Int
data Card = Card Suit Rank deriving (Show, Eq)
和Rank
在这里是完全相同的类型,可以互换使用。使用Int
可以使代码更容易理解,方法是使程序员清楚Rank
代表Int
的位置,而不是用于其他目的的整数。
此方法的主要优点是您可以避免在很多地方包含单词Rank
(例如,将卡写成Rank
而不是Card A 1
,并且定义点不需要对Card A (Rank 1)
上的参数进行模式匹配,等等。)主要缺点是,它模糊了Rank r
与其他整数之间的区别,并使使用{{ 1}},您打算使用这些点,反之亦然。