递归数据类型的其他情况

Additional case for a recursive data type

我目前正在学习 Haskell,我想为货币 Dollar(只是钞票,不是硬币)定义我自己的递归数据类型。

我的尝试是这样的:

data Dollar = One Dollar
             | Two Dollar
             | Five Dollar
             | Ten Dollar
             | Twenty Dollar
             | Fifty Dollar
             | Hundred Dollar

我把这个给我的一个朋友看了,他说看起来不错,但他还告诉我在定义的末尾加上一个 | End。他试图解释为什么有必要,但我无法理解他的思路。也许这里有人有我可以遵循的解释。非常感谢。

您的朋友可能表示在某些时候您需要一个基本案例。给定定义

data Dollar = One Dollar
            | Two Dollar
            | Five Dollar
            | Ten Dollar
            | Twenty Dollar
            | Fifty Dollar
            | Hundred Dollar
            | End

您可以将任意数量的钱表示为 Dollar 值的链表,每个值代表一张账单和“剩余”的钱。 End 将终止列表。例如,

fortyTwoDollars :: Dollar
fortyTwoDollars = Twenty (Ten (Five (Five (Two End))))

但是,我看不出有什么理由将列表混入其中;单个 Dollar 值可以单独表示一张账单,您可以使用 [Dollar] 值表示一个集合。

data Dollar = One
            | Two
            | Five
            | Ten
            | Twenty
            | Fifty
            | Hundred
            
fortyTwoDollars :: [Dollar]
fortyTwoDollars = [Twenty, Ten, Five, Five, Two]

这个不需要递归

描述账单的问题不需要递归类型。让我们从一个清单开始,然后谈谈你的“美元”。

列表

你的bills基本上是特殊列表,构造函数多,没有终止。比如一个普通的列表是这样的:

data List a = SomeElement a List | End

这意味着我们可以构造一个链表,例如:

myList = SomeElement 1 (SomeElement 2 ( End ) )

但是如果我们没有 End 我们就没有办法停止列表。它必须永远SomeElement

otherList = SomeElement 1 (SomeElement 2 ( ... oh no, I have to keep going! ...))

返回“美元”

所以你有这张特殊的美元清单。您可以使用 OneFive,而不是用 SomeElement 1 表示一美元和 SomeElement 5 表示五美元钞票。这允许您构建一个“堆栈”的账单:

myMoney = One (Two (Five ( One ...

但是你不能停止,除非你使列表循环,例如让 ... 成为 myMoney,从而得到无限的 One Two Five One One Two Five One One ....

列表

质疑设计

您可能只想要一些简单的东西:

data Dollar = One | Two | Five

然后您可以使用 [One, Two, Five, One] 等普通列表制作美元列表(类型 [Dollar])。通过声明 One 账单还包含另一美元(这就是 Dollar = One Dollar 的意思),您创建的不是一张账单而是一张账单列表,这似乎没有用。