数据结构(Python语言描述)- 栈

本文详细介绍了栈这种数据结构,包括栈的概念、使用接口、三种应用(如后缀表达式计算、回溯算法和内存管理)、栈的Python实现(数组和链表)及其实现的时间和空间分析,最后通过案例展示了如何使用栈计算后缀表达式。
摘要由CSDN通过智能技术生成

目录

1 栈概览

2 使用栈

2.1 栈接口

2.2 初始化一个栈

2.3 示例应用程序:匹配括号

3 栈的3种应用

3.1 后缀表达式

3.1.1 计算算数表达式

3.1.2 计算后缀表达式

3.1.3 将中缀表达式转换为后缀表达式

3.2 回溯算法

3.3 内存管理

4 栈的实现

4.1 测试驱动程序

4.2 将栈添加到集合层级中

4.3 数组实现

4.4 链表实现

4.5 AbstractStack类的作用

4.6 两种实现的时间和空间分析

5 案例学习:计算后缀表达式

5.1 要求

5.2 分析

5.3 设计

5.4 实现

6 小结


1 栈概览

  • 栈是线性集合,遵从后进先出原则( Last - in first - out , LIFO )原则
  • 栈常用的操作包括压入( push ) 和弹出( pop )

clip_image001

  • 栈的应用
    •  将中缀表达式转换为后缀表达式,并且计算后缀表达式的值
    • 回溯算法
    • 管理计算机内存以支持函数和方法调用
    • 支持应用程序中的撤消功能
    • 维护Web浏览器访问链接的历史记录

2 使用栈

2.1 栈接口

  • 栈不是Python的内建类型,可以用列表代替,但是列表可以在任何位置插入,删除,替换元素,这些违背了栈作为一种抽象数据类型的本意,因此需要为栈定义一种更为严格的接口
栈接口中的方法

               栈方法

作用
s.isEmpty()如果为空返回True,否则返回False
__len__(s)和len(s)相同,返回s中的项的数目
__str__(s)和str(s)相同,返回s的字符串表示
s.__iter__()和iter(s)或for item in s:相同;从底部像顶部,访问s中的每一项
s.__contains__(item)和item in s相同,如果item在s中,返回True,否则返回False
s1.__add__(s2)

和s1+s2相同,返回一个新的栈,其中包含了s1和s2中的项

s.__eq__(anyObject)和s==anyObject相同,如果s等于anyObject,返回True,否则返回False。如果在对应位置的项都是相同的,这两个栈就是相等的
s.clear()将s清空
s.peek()返回s顶部的项。先验条件:s不能为空,如果栈为空的话,将会抛出一个KeyError
s.push(item)在s的顶部添加一项
s.pop()在s的顶部删除一项并返回该项。先验条件:s不能为空。如果栈为空的话,将会抛出一个KeyError
  • pop和peek方法有一个重要的先验条件,如果不满足这个先验条件的话,将会导致一个异常。
  • 这个接口的优点是,用户将知道使用哪一个方法,以及这些方法接收什么参数,而不必管选择了哪一种栈实现
栈操作的效果
操作该操作之后栈的状态返回值说明
s = <Stack Type>()  初始化,栈为空
s.push(a)a 栈只包含一个项a
s.push(b)a b b是栈顶
s.push(c)a b c 

c是栈顶

s.isEmpty()a b cFalse这个栈不为空
len(s)a b c 3栈中包含3个项
s.peek()a b cc返回栈顶的值,但并不删除它
s.pop()a bc删除并返回栈顶的值,b现在成为栈顶
s.pop()ab删除并返回栈顶的值,a现在成为栈顶
s.pop() a删除并返回栈顶的值
s.isEmpty() True栈现在为空
s.peek() KeyError查看一个空的栈会导致一个异常
s.pop() KeyError

弹出一个空的栈会导致一个异常

s.push(d)d d是栈顶

2.2 初始化一个栈

s1 = ArrayStack()
s2 = LinkedStack([20, 40, 60])

2.3 示例应用程序:匹配括号

表达式中匹配的和不匹配的括号
示例表达式状态原因
(...)...(...)匹配 
(...)...(...不匹配末尾漏掉了一个)
)...(...(...)不匹配

最开头的结束),没有一个匹配的开始(;

第2个开始的(,没有对应的结束圆括号

[...(...)...]匹配 
[...(...]...)不匹配方括号的部分没有正确地嵌套
  •  用栈检查一个表达式的步骤如下

  1. 扫描表达式,将开始的括号压入栈中
  2. 当遇到一个结束的括号时,如果栈为空,或者如果栈项的项不是相同的类型开始括号,括号不匹配
  3. 如果是正确类型的括号,从栈顶弹出一项,继续扫描表达式
  4. 当到达表达式未尾时,栈应为空,否则括号不匹配
  • Python代码:
    • index 方法返回列表中项的位置
    • 可以自定义括号的类型
# -*- encoding: utf-8 -*-
# Author: ZFT

"""
Check expressions for matching brackets
"""

from Data_structure.Chapter7.linkedstack import LinkedStack

def bracketBalance1(exp):
    """exp is a string that represents the expression."""
    stk = LinkedStack()           # Create a new stack
    for ch in exp:                # Scan across the expression
        if ch in ['[', '(']:      # Push an opening bracket
            stk.push(ch)
        elif ch in [']', ')']:    # Process a closing bracket
            if stk.isEmpty():     # Not balanced
                return False
            chFromStack = stk.pop()

            # Brackets must be of same type and match up
            if ch == ']' and chFromStack != '[' or \
                ch == ')' and chFromStack != '(':
                return False
    return stk.isEmpty()           # They all matched up

def bracketBalance2(exp, startBracketList = ['(', '['],endBracketList = [')',']'] ):
    """exp is a string that represents the expression.
       startBracketList is the list of start bracket list.
       endBracketList is the list of end bracket list.
       precondition:startBracketList must have the same length of endBracketList.
       raise: Exception if startBracketList don't have the same length of endBracketList.
    """
    if len(startBracketList) != len(endBracketList):
        raise  Exception("The startBracketList must have the same length with the endBracketList.")

    stk = LinkedStack()
    for ch in exp:
        if ch in startBracketList:
            stk.push(ch)
        elif ch in endBracketList:
            if stk.isEmpty():
                return False
            chFromStack = stk.pop() 
            if chFromStack != startBracketList[endBracketList.index(ch)]:
                return False
    return stk.isEmpty()

def main():
    exp = input("Enter a bracketed expression:")
    if bracketBalance1(exp):
        print ("OK")
    else:
        print("Not OK")


if __name__ == "__main__":
    main()

3 栈的3种应用

3.1 后缀表达式

3.1.1 计算算数表达式

  • 首先,将一个表达式从我们熟悉的中缀形式转换为后缀形式,然后,计算这种后缀形式
  • 在中缀形式中,每个运算符位于两个运算数之间
  • 在后缀形式中,运算符紧跟在其运算数之后
一些中缀和后缀表达式
中缀形式后缀形式
343434
34 + 2234 22 +56
34 + 22 * 234 22 2 * +78
34 * 22 + 234 22 * 2 +750
(34 + 22)* 234 22 + 2 *112
  • 在两种形式之中,运算数都按照相同的顺序出现。然而,运算符则不是按照相同的顺序出现的。中缀形式有时候需要圆括号,而后缀形式不需要。中缀形式的计算涉及优先级的规则,而后缀形式则只要一遇到运算符就应用它
  • 在中缀表达式中,圆括号和运算符优先级的使用是为了方便人们读写和书写表达式。通过删除这些圆括号,等价的后缀式一以一种更加容易和高效计算的格式展示给计算机 

3.1.2 计算后缀表达式

  • 计算一个后缀表达式涉及3个步骤:
    • 从左到右的遍历表达式,遇到运算数,则将其压入数字栈中
    • 碰到一个运算符时,从数字栈中弹出两个运算数,对其应用运算符,并将所得的结果压入数字栈
    • 继续遍历,直到到达表达式的未尾,此时,数字栈中只剩表达式的值
# token 指的是一个运算数或一个运算符
Create a new stack
While there are more tokens in the expression
    Get the next token
    If the token is an operand
        Push the operand onto the stack
    Else if the token is an operator
        Pop the top two operands from the stack
        Apply the operator to the two operands just popped
        Push the resulting value onto the stack
Return the value at the top of the stack   

计算一个后缀表达式的过程

后缀表达式: 4 5 6 * + 3 -                                          结果值:31

后缀表达式目前扫描到的部分 运算数栈 说明     还没有扫描到标记。栈为空 4 4 将运算数4压入栈 4 5  4 5 将运算数5压入栈 4 5 6 4 5 6 将运算数6压入栈 4 5 6 * 4 30 用栈顶的两个运算数的乘积来替换 4 5 6 * + 34 用栈顶的两个运算数的和来替换 4 5 6 * + 3 34 3 将运算数3压入栈 4 5 6 * + 3 - 31 用栈顶的两个运算数的差来替换     弹出最终的结果

3.1.3 将中缀表达式转换为后缀表达式

  • 将中缀表达式转换为后缀表达式的步骤:
  1. 开始的时候,有一个空的后缀表达式和一个空的栈,栈用来保存运算符和左圆括号
  2. 从左向右扫描中缀表达式
  3. 遇到一个运算数的时候,将其添加到后缀表达式的后面
  4. 遇到一个左圆括号的时候,将其压入到栈中
  5. 遇到一个运算符,从栈中弹出和它具有相等的或更高优先级的所有运算符,将它们添加到后缀表达式的末尾,然后,将扫描到的运算符压入栈中
  6. 遇到一个右圆括号的时候,将运算符从栈中移动到后缀表达式中,直到遇到了与之匹配的左圆括号,并将其丢弃
  7. 遇到中缀表达式结束的时候,将栈中剩下的运算符都转移到后缀表达式之中

一个中缀表达式转换为后缀表达式的过程

中缀表达式:4 + 5 * 6 - 3                  后缀表达式:4 5 6 * + 3

   当前扫描到的中缀表达式

                的位置

运算符栈 后缀表达式 说明       还没有看到标记,堆栈和后缀表达式都是空的 4   4 将4添加到后缀表达式 4 +  + 4 将+压入运算符栈 4 + 5 + 4 5 将5添加到后缀表达式 4 + 5 * + * 4 5 将*压入运算符栈 4 + 5 * 6 + *  4 5 6 将6添加到后缀表达式 4 + 5 * 6 - - 4 5 6 * + 将*和+弹出栈,并将它们添加到后缀表达式,并将-压入运算符栈 4 + 5 * 6 - 3 - 4 5 6 * + 3 将3添加到后缀表达式 4 + 5 * 6 - 3   4 5 6 * + 3 - 将剩下的运算符从栈中弹出,并将其添加到后缀表达式

从中缀表达式到后缀表达式的转换过程(带括号)

中缀表达式:(4 + 5) *(6 - 3)                    后缀表达式:4 5 + 6 3 - * 

    当前扫描到的中缀表达式

                的位置

运算符栈 后缀表达式 说明       还没有看到标记,堆栈和后缀表达式都是空的 ( (   将(压入运算符栈 (4  ( 4 将4添加到后缀表达式 (4 + ( + 4 将+压入运算符栈 (4 + 5 ( + 4 5 将5添加到后缀表达式 (4 + 5)   4 5 + 弹出栈直到遇到(,并且将运算符添加到后缀表达式 (4 + 5)* *  4 5 + 将*压入运算符栈 (4 + 5) *( *( 4 5 + 将(压入运算符栈 (4 + 5) *(6 *( 4 5 + 6 将6添加到后缀表达式 (4 + 5) *(6 - *( - 4 5 + 6 将-压入运算符栈 (4 + 5) *(6 - 3 *( - 4 5 + 6 3 将3添加到后缀表达式 (4 + 5) *(6 - 3) * 4 5 + 6 3 - 弹出栈直到遇到(,并且将运算符添加到后缀表达式 (4 + 5) *(6 - 3)   4 5 + 6 3 - * 将剩下的运算符从栈中弹出,并将其添加到后缀表达式

3.2 回溯算法

  • 回溯算法从一个预定义的起始状态开始,随后从一个状态移动到另一个状态,以搜索想要的最终状态
  • 在任何状态下,如果有多个选择,则会随机选取一种状态,然后继续
  • 如果算法到达了不希望结果的一个状态,它会回到上一个拥有一个末探索的、可替代选项的位置,并尝试这个可替代选项
  • 最后,要么算法到达了想要的结果,要么穷尽了对所有算法的搜索
  • 使用栈实现回溯算法的伪代码:
Create an empty stack
Push the starting state onto the stack
While the stack is not empty
    Pop the stack and examine the state
    If the state represents an ending state
        Return SUCCESSFUL CONCLUSION
    Else if the state has not been visited previously
        Mark the state as visited
        Push onto the stack all unvisited adjacent states
Return UNSUCCESSFUL CONCLUSION
  • 在很多的游戏逻辑和谜题求解程序中,可以看到广泛的回溯算法的应用 

3.3 内存管理

  • 在程序执行过程中,其代码和数据都会占用计算机的内存。计算机的运行时间系统必须记录对程序员来说不可见的各种细节。这包括:
    • 将变量和内存中存储的数据对象关联起来,以便引用这些变量的时候能够找到它们
    • 记住所调用的方法或函数中的指令地址,以便当函数或方法完成执行的时候能够,执行返回到下一条指令
    • 为函数或方法的参数和临时变量分配内存,这些参数和临时变量只有在方法或函数执行的时候才存在
  • PVM( Python Virtual Machine ) 运行时的环境架构

clip_image007

  • 从下往上,这些区域包括:
    • Python虚拟机(PVM),它执行一个Python程序。PVM有两个内部变量,名为locationCounter和basePtr。locationCounter指向PVM下一步将要执行的程序。basePtr指向基本活动记录的顶部。
    • 程序的所有子例程的字节码
    • 程序的模块和类变量
    • 调用栈。每次调用子例程的时候,都会创建一条活动记录并且将其压入到调用栈。当一个子例程完成执行并且将控制返回到调用它的子例程的时候,该活动记录从栈中弹出。栈中的活动记录的总数等于在执行的各种阶段当前调用的总数。
    • 未使用的内存。这个区域的大小会增加或缩小以响应调用栈和对象堆的需求
    • 对象堆。在Python中,所有的对象都存在于一个叫做堆的区域中。当对象实例化的时候,PVM必须在堆上寻找用于该对象的空间,并且当对象不再需要的时候,PVM的垃圾收集程序会恢复该空间以供将来使用。当空间变得较少的时候,堆会进一步扩展到标记未使用内存的区域
  • 调用子例程的步骤
    • 创建子例程的活动记录,并将其压入栈
    • 在标记为 Prev basePtr 的区域中保存 basePtr 的当前值,并将 basePtr 设置为新的活动记录位置
    • 在 Retrun Address 中,保存 locationCounter 的当前值,并将 locationCounter 设置为被调用子例程的第1条指令
    • 将调用参数复制到 Parameters 的区域中
    • 开始执行位于 locationCounter 所指示位置的子程序
  • 在子例程执行的过程中,通过给 basePtr 加上一个偏移量,以引用活动记录中的临时变量和参数
  • 在返回之前,一个子例程将其返回值存储在Return Value 的位置。
  • 当子例程执行完成后,PVM执行以下操作:
    • 使用活动记录中存储的值来恢复locationCounter 和 basePtr 的值,从而重新建立调用子例程所需的设置
    • 从调用栈中弹出活动记录
    • 在locationCounter 所指示的位置,继续指定调用子例程

4 栈的实现

4.1 测试驱动程序

  • 代码:
# -*- encoding: utf-8 -*-
# Author: ZFT

from Data_structure.Chapter7.arraystack import ArrayStack
from Data_structure.Chapter7.linkedstack import LinkedStack

def test(stackType):
    # Test an implementation with the same code.
    s = stackType()
    print("Length:", len(s))
    print("Empty:",s.isEmpty())
    print("Push 1-10")
    for i in range(10):
        s.push(i + 1)
    print("Peeking:", s.peek())
    print("Items(bottom to top):", s)
    print("Length:", len(s))
    print("Empty:", s.isEmpty())
    theClone = stackType(s)
    print("Items in clone(bottom to top)", theClone)
    theClone.clear()
    print("Length of clone after clear:",len(theClone))
    print("Push 11")
    s.push(11)
    print("Popping items(top to bottom):", end = " ")
    while not s.isEmpty():
        print(s.pop(), end = " ")
    print("\nLength:", len(s))
    print("Empty:", s.isEmpty())

def main():
    test(LinkedStack)

if __name__ == '__main__':
    main()

4.2 将栈添加到集合层级中

  • 一个集合可以实现可以通过成为集合层级的一部分,从而自由地获取一些功能
  • 两个栈实现ArrayStack和LinkedStack可以按照相同的方式来处理。它们都实现了相同的、名为StackInterface的接口。它们都是AbstractStack的子类,而AbstractStack类又是AbstractCollection的一个子类。它们从AbstractStack类继承了add方法,从AbstractCollection继承了_size变量、isEmpty()、__len__、__str__、__add__和__eq__方法。因此,在ArrayStack和LinkedStack中,要实现的方法只有__init__、peek、push、pop、clear和__iter__方法
  • 栈资源的层级图如下:

clip_image008

4.3 数组实现

  • 在数组的尾部来添加和删除节点
  • 当数组填满或者装填因子小于 1/4 时,需要对数组的容量进行调整
  • 代码:
# -*- encoding: utf-8 -*-
# Author: ZFT

from Data_structure.Chapter4.arrays import Array
from Data_structure.Chapter7.abstractstack import AbstractStack

class ArrayStack(AbstractStack):
    """An array-based stack implementation."""
    DEFAULT_CAPACITY = 10          # For all array stacks

    def __init__(self, sourceCollection = None):
        """Sets the initial state of self, which includes the
           contents of sourceCollection, if it's present.
        """
        self._items = Array(ArrayStack.DEFAULT_CAPACITY)
        AbstractStack.__init__(self, sourceCollection)


    # Accessors
    def __iter__(self):
        """Supports iteration over a view of self.
           Visits items from bottom to top of stack.
        """
        cursor = 0
        while cursor < len(self._items):
            yield self._items[cursor]
            cursor += 1

    def peek(self):
        """Returns the item at top of the stack.
           Precondition: the stack is not empty.
           Raises KeyError if the stack is empty.
        """
        if self.isEmpty():
            raise KeyError("The stack is Empty.")
        return self._items[len(self._items) - 1]

    # Mutator
    def clear(self):
        """Makes self become empty."""
        self._size = 0
        self._items = Array(ArrayStack.DEFAULT_CAPACITY)

    def push(self, item):
        """Inserts item at top of the stack."""
        # Resize array size here if necessary
        self._items[len(self._items)] = item
        self._size += 1

    def pop(self):
        """Removes and returns the item at top of the stack.
           Precondition: the stack is empty.
           Raises KeyError if the stack is empty.
           PostCondition: the top item is removed from the stack.
        """
        if self.isEmpty():
            raise KeyError("The stack is Empty.")

        oldItem = self._items[len(self._items) - 1]
        self._size -= 1

        # Resize the array size here if necessary
        return oldItem

4.4 链表实现

  • 在链表序列的头部来添加和删除节点
  • 迭代遍历
    • 尽管链表结构支持简单的push和pop,但__iter__方法的实现还是很复杂的,因为必须从链表结构的尾部到头部来访问各项。遗憾的是,要便利一个单链表结构,必须从其头部开始并且按照next链接直到其尾部
    •  可以使用递归。在__iter__方法中,我们创建了一个临时的列表,并且定义了一个递归的辅助函数,它接收一个节点作为参数。在初次调用该函数的时候,参数节点是栈的链表结构的头(变量self._items)。如果这个节点不为None,我们使用该节点的next字段递归地调用该函数,从而一直向前直到到达结构的尾部。当这个调用返回的时候,我们将节点的数据添加到临时列表中。当辅助函数的顶级调用返回的时候,我们就返回该列表上的一个迭代器
  • 代码:
# -*- encoding: utf-8 -*-
# Author: ZFT

from Data_structure.Chapter4.node import Node
from Data_structure.Chapter7.abstractstack import AbstractStack

class LinkedStack(AbstractStack):
    """Link-based stack implementation."""
    def __init__(self, sourceCollecion = None):

        self._items = None
        AbstractStack.__init__(self, sourceCollecion)

    # Accessors
    def __iter__(self):
        """Supports iteration over a view of self.
           Visits items from bottom to top of stack."""
        def visitNodes(node):
            if not node is None:
                visitNodes(node.next)
                tempList.append(node.data)

        tempList = list()
        visitNodes(self._items)
        return iter(tempList)

    def peek(self):
        """Returns the item at top of the stack."""
        if self.isEmpty():
            raise KeyError("The stack is empty.")
        return self._items.data

    # Mutator
    def clear(self):
        """Makes self become empty."""
        self._items = None
        self._size = 0

    def push(self, item):
        """Inserts item at top of the stack."""
        self._items = Node(item, self._items)
        self._size += 1

    def pop(self):
        """Removes and returns the item at top of the stack.
           Precondition: the stack is empty.
           Raises KeyError if the stack is empty.
           PostCondition: the top item is removed from the stack."""

        if self.isEmpty():
            raise KeyError("The stack is empty.")
        oldItem = self._items.data
        self._items = self._items.next
        self._size -= 1
        return oldItem

4.5 AbstractStack类的作用

  • 定义 add 类,以避免修改AbstractCollection类
  • 代码:
# -*- encoding: utf-8 -*-
# Author: ZFT

from Data_structure.Chapter7.abstractcollection import AbstractCollection
class AbstractStack(AbstractCollection):
    """An abstract stack implementation."""
    def __init__(self, sourceCollection = None):
        """Sets the initial state of self, which incudes the
           contents of sourceCollection, if it's present."""

        AbstractCollection.__init__(self, sourceCollection)

    # Mutator
    def add(self, item):
        """Add item to self."""
        self.push(item) 

4.6 两种实现的时间和空间分析

  • 时间性能
    • 除了 __iter__ 方法,所有的栈方法的运行时间均不大于 O(1)
      • 数组实现,在数组容量翻倍或者减半时,运行时间会增加到 O(n)
        • 使用数组栈时,需要决定响应时间上的波动是否可以接受
    • __iter__方法都是线性时间运行的
  • 空间性能
    • 链表实现使用了迭代,由于系统调用栈,从而导致内存线性增长
    • 当数组的填充因子大于 1/2 时,数组的内存性能优于链表

5 案例学习:计算后缀表达式

5.1 要求

  • 编写交互式程序,来计算后缀表达式

5.2 分析

  • 用户交互
    • 用户在提示符下输入一个表达式,程序显示结果。输入表达式时,限制在一行文本之内,标记之间可以有任意空白。用户按下Enter或 Return键后,按照每个标记之间只有一个空格的形式输出表达式。并且在后面新的一行中,输出表达式的值,或者是错误信息加上当前运行状态。
    • 可能包含的错误
      • 表达式包含了太多的运算数
      • 表达式包含的运算数不够
      • 表达式包含了不识别的标志
      • 表达式包含了除以0的情况

5.3 设计

  • 计算程序的交互图

clip_image009

  • PFEvaluatorView类的实例变量和方法
PFEvaluatorView()
    Create and saves a reference to the model. 

run()
    while True:
        Retrieve the expressions string from keyboard.
        Send it to the model for formatting and print the format string.
        Send it to the model for evaluation.
        Either print the value or catch exceptions raised by the evaluator.
        ask the model for the associated details, and display error.
        messages.          
  • PFEvaluatorModel 类的实例变量和方法
    • Model 模型需要与扫描程序和计算程序进行通信。
    • 方法如下:
format( expressionStr ):
    Instantiate a scanner on the expression string.
    Build a response string by iterating across the scanner and appending a
    string representation of each token to the response string.
    Return the response string.

evaluate( expressionStr ):
    Ask the evaluator to evaluate the expression string.
    Return the value.

evaluationStatus():
    Ask the evaluator for its status.
    Return the status
  • PFEvaluator 类的实例变量和方法
    • 计算程序的属性包括一个栈,一个扫描程序和一个名为 expressionSoFar的字符串变量
    • 方法如下:
PFEvaluator( scanner )
    Intialize expressionSoFar
    Instantiate an ArrayStack
    Save a reference to the scanner

evaluate()
    Iterate across the scanner and evaluate the expression.
    Raise exception in the following situation:
        The scanner is None or empty
        There are too many operands
        There are too few operands
        There are unrecognizable tokens.
        A divide by 0 exception is raised by the PVM

evaluationStatus()
    Return a multipart string that contains the portion of the expression
    processed and the contents of the stack.
  • Scanner 类的实例变量和方法
Scanner( sourceStr ):
    Save a reference to the string that will be scanned and tokenized

hasNext()
    Return True if the string contains another token and False otherwise.

next()
    Return the next token. Raise an exception if hasnext() return False.
  • Token 类的实例变量与方法
    • Type定义
UNKNOWN = 0     #unknow
INT = 4         #interger
MINUS = 5       #minus operator
PLUS = 6        #plus operator
MUL = 7         #multiply operator
DIV = 8         #divide operator
  • 方法如下:
Token( vlaue ):
    Construct a new integer token with the specified value

Token(ch):
    if ch is an operator( + - * / ), then construct a new operator token;
    otherwise, construct a token of unknown type.

getType()
    return a token's type

getValue()
    return a token's value.

isOperator():
    Return True if the token is an operator, and False Otherwise

__str__():
    Return the token's numeric value as a string if the token is an
    integer; otherwise, return the token's character representation.

5.4 实现

# -*- encoding: utf-8 -*-
# Author: ZFT

"""
Tokens for processing expressions.
"""

class Token(object):

    UNKNOWN = 0               # unknown

    INT = 4                   # integer

    MINUS = 5                 # minus    operator
    PLUS = 6                  # plus     operator
    MUL = 7                   # multiply operator
    DIV = 8                   # divide   operator

    FIRST_OP = 5              # first operator code

    def __init__(self, value):
        if type(value) == int:
            self._type = Token.INT
        else:
            self._type = self._makeType(value)

        self._value = value

    def isOperator(self):
        return self._type >= Token.FIRST_OP

    def __str__(self):
        return str(self._value)

    def getType(self):
        return self._type

    def getValue(self):
        return self._value

    def _makeType(self, ch):
        if ch == '*':
            return Token.MINUS
        elif ch == '+':
            return  Token.PLUS
        elif ch == '-':
            return  Token.MINUS
        elif ch == '/':
            return  Token.DIV
        else:
            return Token.UNKNOWN
# -*- encoding: utf-8 -*-
# Author: ZFT

"""
Defines PEEvaluatorModel and PEEvaluator
"""

from Data_structure.Chapter7.PEEvaluatorModel.tokens import Token
from Data_structure.Chapter7.PEEvaluatorModel.scanner import Scanner
from Data_structure.Chapter7.arraystack import ArrayStack

class PEEvaluatorModel(object):

    def evaluate(self, sourceStr):
        self._evaluator = PEEvaluator(Scanner(sourceStr))
        value = self._evaluator.evaluate()
        return value

    def format(self, sourceStr):
        normalizedStr = ""
        scanner = Scanner(sourceStr)
        while scanner.hasNext():
            normalizedStr += str(scanner.next()) + " "

        return normalizedStr

    def evaluationStatus(self):
        return str(self._evaluator)




class PEEvaluator(object):

    def __init__(self, scanner):
        self._expressionSoFar = " "
        self._operandStack = ArrayStack()
        self._scanner = scanner

    def evaluate(self):
        while self._scanner.hasNext():
            currentToken = self._scanner.next()
            self._expressionSoFar += str(currentToken) + " "
            if currentToken.getType() == Token.INT:
                self._operandStack.push(currentToken)
            elif currentToken.isOperator():
                if len(self._operandStack) < 2:
                    raise AttributeError("Too few operands on the stack.")
                t2 = self._operandStack.pop()
                t1 = self._operandStack.pop()
                result = Token(self._computeValue(currentToken, t1.getValue(), t2.getValue()))

                self._operandStack.push(result)
            else:
                raise AttributeError("Unknown token type.")

            if len(self._operandStack) > 1:
                raise AttributeError("Too many operands on the stack.")
            result = self._operandStack.pop()
            return result.getValue()

    def __str__(self):
        result = "\n"
        if self._expressionSoFar == "":
            result += "Portion of expression processed: none\n"
        else:
            result += "Portion of expression processed: "+ self._expressionSoFar + "\n"

        if self._operandStack.isEmpty():
            result += "The stack is empty."
        else:
            result += "Operands on the stack: " + str(self._operandStack)
        return result

    def _computerValue(self, op, value1, value2):
        result = 0
        theType = op.getType()
        if theType == Token.PLUS:
            result = value1 + value2
        elif theType == Token.MINUS:
            result = value1 - value2
        elif theType == Token.MUL:
            result = value1 * value2
        elif theType == Token.DIV:
            result = value1 / value2
        else:
            raise AttributeError("Unknown operator")
        return result


6 小结

  1. 栈是线性的集合,它只允许从一端访问,这一端叫作栈顶
  2. 栈上的其他操作,包括查看顶部的元素,确定元素的数目,判断栈是否为空,以及返回一个字符串表示
  3. 在应用中,栈以后进先出的方式管理数据项。这些应用包括匹配表达式中的方括号符号、计算后缀表达式、回溯算法,以及虚拟机上的子例程调用内存管理
  4. 数组和单链表结构支持栈的简单实现

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值