2010-01-13 48 views
9

我们已经读过或听说过堆栈类,但我们中的很多人可能从未找到使用LIFO对象的理由。我很好奇听到使用此对象的真实世界解决方案以及为什么。您使用了“堆栈”对象(.Net)的真实世界使用

http://msdn.microsoft.com/en-us/library/system.collections.stack.aspx

我最近看到其中一个程序员使用的堆栈,以保持自己的当前位置的轨迹,同时遍历分层数据源的例子。当他向下移动时,他将自己的位置标识符推到堆栈上,当他移回来时,他将物品从堆叠中弹出。我认为这是一个非常有效的方式来跟踪他目前的位置在一个mamoth等级。我从来没有见过这个。

其他人有任何例子吗?

+0

是的 - 先使用堆栈深度。你知道你首先使用广度排队吗? – RichardOD 2010-01-13 16:08:48

+2

这不是一个真正的.net特定问题。堆栈是一种抽象的数据类型,人们在(可能)每种语言中使用它们。 – 2010-01-13 16:20:42

+0

我一直在使用堆栈。你可以在必要时使用它们。 – 2010-01-13 16:55:56

回答

16

我用它们来跟踪撤消和重做动作。

我使用的界面是这样的:

interface ICommand 
{ 
    void Execute(); 
    void Undo(); 
    string Description { get; } 
} 

撤消和重做是Stack<ICommand>型两种。然后我为给定的动作创建一个具体的类。在该类的构造函数中,我传入了需要保留的任何信息。 Execute最初做了这个动作,并且也重做了它; Undo明显地解雇了它。它的工作原理如下:

  • 撤消操作:弹出撤消堆栈并将其添加到重做堆栈。
  • 重做撤消操作:弹出重做堆栈并再次添加到撤消堆栈。
  • 执行新操作:添加到撤消堆栈并清除重做堆栈(因为状态不再一致)。

我发现,你必须要小心,你真的撤销这事。例如,假设你有一个包含两个列表框的UI,并且每个列表框中都有五个项目。您的操作可能是单击一个按钮将左列表中的所有内容移动到右列表中(因此它现在有十个,而左列表为零)。

撤消操作是而不是将所有内容都移回;撤消行动是只移回你实际移动的五个,并留下其他人。

7

Stacks are used whenever a stored procedure/sub-routine is called to store local variables and return address.

栈用于表达式求值(例如,在一个计算器,或编译器),第一表达式被转换为RPN然后简单堆栈机用于评价。当你看到一个操作数将它压入堆栈时,它的工作原理如下。当你看到一个操作符弹出操作数并评估时。

example 

5 6 + 3 * 

steps- 
see 5 push 5 
see 6 push 6 
see + pop 2 times and apply + get 11 push 11 
see 3 push 3 
see * pop 2 times and apply get 33 push 33 

result is on the top of the stack. 
+0

您可以通过推广语言解释器经常使用堆栈来扩展此答案。 – Toad 2010-01-13 16:16:33

+0

err ...我说他们用于表达评估 - 因此任何表达评估使用它们,然后我给出了两个例子。 – Hogan 2010-01-13 16:19:39

+0

@downvoter:随时发表评论 – Hogan 2010-01-13 16:21:46

5

如果您有递归算法,您通常可以使用堆栈重写它们。 (因为递归算法隐含已经使用堆栈)

+0

嗯....我不认为这是一个重写。这是一回事。 – Hogan 2010-01-13 16:16:52

+0

是真实的,但有时它可以节省大量的参数,您现在可以保持本地 – Toad 2010-01-13 16:18:56

+2

-1,这根本不是现实世界。如果它是尾递归的,那么你只需使用while循环;否则,使用Stack对象比使用递归更有效(且速度更慢)。 – 2010-01-13 16:51:40

0

我发现堆在多线程aplications非常有用跟踪在反时限时装状态的...

每个线程的同步共享提出的状态消息堆栈,你有发生了什么“面包屑”。

不太.NET,但...这是我oppinion =)

+0

由于在这个例子中,事物的顺序并不重要,所以队列也可以很好地工作 – Toad 2010-01-13 16:17:54

+0

如果你想以面包屑的方式调试东西,这很重要。像采取每一步你做了倒退。我想他问我们任何人发现有用的例子... =) – 2010-01-13 16:26:23

0

为了提供一个具体的例子来照亮别人所评论的:实现Z机器翻译三种不同的堆栈应使用。一个调用堆栈以及几种不同类型的对象堆栈。 (具体要求可以在here.找到)注意,就像所有这些例子一样,使用堆栈并不是严格的要求,这是明显的选择。

调用堆栈跟踪对子例程的递归调用,而对象堆栈用于跟踪内部项目。

3

您可以验证需要平衡令牌的字符串输入。想想LISP:

(+ (- 3 2) (+ (+ 4 5) 11)) 

当你点击开括号:

stack.Push("(") 

然后,当你打了一个闭合括号:

stack.Pop() 

如果有剩余的筹码任何标记,当你'完成了,它不平衡。

您可以获得更多信息并验证输入HTML中的正确嵌套。在一个高度人为的例子:

//see opening body 
stack.Push("body") 

//see opening div 
stack.Push("div") 

//see opening p 
stack.Push("p") 

///see closing div 
if(!stack.Pop().Equal("div")){ 
    //not balanced 
} 
+0

这也适用于解析XML文档 – Scoregraphic 2010-01-14 13:08:20

1

在一个现实生活中的使用中,后记发电机类具有“current_font”状态,作为字体为其绘制文本的任何操作。有时候一个函数需要暂时设置字体,但是要让它回到原来的样子。我们可以只使用一个临时变量来保存和恢复字体:

def draw_body 
    old_font = ps.get_font 
    ps.set_font('Helvetica 10') 
    draw_top_section 
    draw_bottom_section 
    ps.set_font(old_font) 
end 

但到了第三次,你这样做,你会想停止重复自己。因此,让我们让PS对象保存和恢复我们的字体:

class PS 

    def save_font 
    old_font = get_font 
    end 

    def restore_font 
    set_font(old_font) 
    end 

end 

现在主叫方变为:

def draw_body 
    ps.save_font 
    ps.set_font('Helvetica 10') 
    draw_top_section 
    draw_bottom_section 
    ps.restore_font 
end 

这工作正常,直到我们使用相同的模式内通过所谓的子程序之一draw_page:

def draw_top_section 
    ps.save_font 
    ps.set_font('Helvetica-bold 14') 
    # draw the title 
    ps.restore_font 
    # draw the paragraph 
end 

当draw_top_section称之为 “save_font”,它则会覆盖已保存的draw_page的字体。现在是时候使用栈:

def PS 

    def push_font 
    font_stack.push(get_font) 
    end 

    def pop_font 
    set_font(font_stack.pop) 
    end 

end 

而且在来电:

def draw_top_section 
    ps.push_font 
    ps.set_font('Helvetica-bold 14') 
    # draw the title 
    ps.pop_font 
    # draw the body 
end 

有进一步的改进可能,如具有PS类自动保存和恢复的字体,但它不是必要进入那些看到一个堆栈的价值。

0

在计算机图形类(不是.NET)中,我们使用Stack来跟踪画面上绘制的对象。这使得每次刷新都可以在屏幕上重新绘制所有对象,并且可以跟踪每个对象的顺序或“z层”,因此当它们移动时它们可以重叠其他对象。