直观地说,我们认为堆栈是我们可以压入值的东西,其顶部值是最后压入的东西,如果我们弹出一个我们已经压入值的堆栈,我们就有了原始堆栈。
这种直觉可以转换为抽象的代数,
使用 Push 和 Pop 表示对称为 Stack 的数据对象的任何计算,涉及元素:
algrebra Stack
{
data Stack;
data Element;
Empty() -> Stack;
Push(Stack,Element) -> Stack;
Pop(Stack) -> Stack;
Top(Stack) -> Element;
axiom Top(Push(s,e))==e;
axiom Pop(Push(s,e))==s;
axiom Pop(Empty())==undefined;
}
这说明如果你能在你的系统中找到实体,
- 您声称其中一些是堆栈类型
- 您声称其中一些是元素,
- 您声称是 Push 的一些功能,
- 您声称的一些函数是 Pop、Top、Empty 等。
那么各种函数的签名与代数中的签名匹配,
并且如果您应用各种功能,它们将表现为
公理指定。 (您选择的实体是“同构的”
到代数)。
这种观点的优点是它没有说明
各种实体的实现,同时仍然抓住本质。
该代数涵盖了其他答案中讨论的各种工件;
应该清楚的是,用作堆栈的线性存储区域是堆栈,
使用堆栈的链表是堆栈,一端使用的双端队列是堆栈,
那一系列用作栈的磁盘块就是栈,...
真正酷的是你可以重命名所有的数据类型和函数
在堆栈代数中,但仍然可以识别堆栈,因为重命名
对同构没有影响(因为一致的重命名只是另一种同构,
和同构组成产品同构)。任何匹配的东西
这个规范是一个堆栈!
实现中的函数名称可以是任何东西,并且
但仍然匹配,并且我们可以重命名抽象代数函数名称
任何东西,仍然匹配,允许我们按照惯例给出抽象
代数函数命名我们认为具有代表性的函数。 “推”
和“pop”是按惯例选择的。
所以我想说,你可以称你的名字你的实现功能任何你
喜欢,但称他们为 Push 和 Pop,如果他们的行为实现了这个代数。
因此,可以通过正式规范定义堆栈。您可以定义任何其他数据结构
也这样。
太糟糕了,我们在记录真实代码时没有做更多的事情。