代数数据类型(ADT)表示固定数量的具名情况,ADT 的每个值都恰好对应其中一个情况。
ADT 用data关键字定义,各个情况之间用竖线(|)分隔。如果所有情况都没有关联数据,这种 ADT 就类似其他语言通常所说的_枚举_(或 enum)。
data Season
= Spring
| Summer
| Autumn
| Winter
ADT 的每个情况都可以选择性地关联数据,不同情况关联的数据类型也可以不同。情况关联了数据时,就必须使用构造函数。
data Number
= NInt Int --'NInt' is the constructor for an Int Number.
| NFloat Float --'NFloat' is the constructor for an Float Number.
| Invalid --'Invalid' does not have data associated to it.
要为某个特定情况创建值,只需引用它的名字(例如 NInt 22)。情况名本身就是构造函数,因此关联的数据可以像普通的函数实参一样传入。
ADT 具有_结构相等性_,也就是说,属于同一情况、且(可选的)数据也相同的两个值彼此等价。
虽然可以用if/else表达式来处理 ADT,但推荐的做法是使用模式匹配,也就是 case 语句:
add1 :: Number -> String
add1 number =
case number of
NInt i -> show (i + 1)
NFloat f -> show (f + 1.0)
Invalid -> error "Invalid input"
在这个练习中,今天是情人节,你正在计划和伴侣一起做什么。你的伴侣有很多想法,并让你给这些想法打分,以便找出最好的活动。
以下是你的伴侣提出的想法:
你有六个任务,帮你选出情人节的活动。
对于伴侣提出的每个想法,你都要从三个选项中选一个来回应:yes、no 或 maybe。
定义Approval代数数据类型,用下面三种情况来表示这些选项:Yes、No或Maybe。
你的伴侣选了两家可能的餐厅:一家做韩式料理,另一家做土耳其料理。
定义Cuisine代数数据类型,用下面两种情况来表示这两家餐厅:Korean或Turkish。
可供选择的电影太多了,为了缩小范围,你的伴侣还列出了自己偏好的电影类型。
定义Genre代数数据类型,来表示以下几种类型:Crime、Horror、Romance或Thriller。
正如前面提到的,你的伴侣想出了五种可能的活动:玩桌游、放松一下、看电影、去餐厅吃饭和散步。
定义Activity代数数据类型来表示这些活动类型:
BoardGame:没有关联数据。Chill:没有关联数据。Movie:以它的Genre作为关联数据。Restaurant:以它的Cuisine作为关联数据。Walk:有一个Int作为关联数据,表示要走的公里数。最后,你可以给伴侣的想法打分了。以下是你对伴侣想法的感受:
实现一个名为rateActivity的函数,它接收一个Activity值,并根据上面的感受返回对应的Approval。例如:
rateActivity (Restaurant Turkish)
-- -> Maybe