无法解析数据/新类型声明中的数据构造函数

时间:2019-04-06 05:32:28

标签: haskell

我的卡片类型为西装和等级,

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不能是类型构造函数以及如何创建正确的函数

谢谢

1 个答案:

答案 0 :(得分:1)

看起来好像是这样:

data Suit = A | B

仅将一种类型Suit定义为任意对象的集合/集合。但是,实际上,它定义了三件事:类型Suit和两个构造函数AB用于创建该类型的值。

如果定义:

data Rank = 1 | 2

实际上是可行的,它不会将Rank定义为数字12的集合,而是会重新定义数字{{ 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}},您打算使用这些点,反之亦然。