您使用过“堆栈”对象(.Net)的真实世界使用情况

时间:2010-01-13 16:05:37

标签: algorithm language-agnostic data-structures

我们已经阅读或了解了堆栈类,但是我们中的许多人可能从未找到使用LIFO对象的理由。我很想知道使用这个对象的真实解决方案及其原因。

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

我最近看到一个例子,程序员在遍历分层数据源时使用堆栈来跟踪他当前的位置。当他向下移动层次结构时,他将位置标识符推到堆栈上,当他向后移动时,他将物品从堆栈中弹出。我认为这是一种非常有效的方式来跟踪他在乳房等级中的当前位置。我以前从未见过这个。

其他人有任何例子吗?

10 个答案:

答案 0 :(得分:17)

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

我使用这样的界面:

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

撤消和重做都属于Stack<ICommand>类型。然后我为给定的动作创建一个具体的类。在类的构造函数中,我传递了我需要保留的任何信息。 Execute最初执行操作,并重做它;显然,Undo撤消了它。它的工作原理如下:

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

我发现你必须要小心你正在彻底解决所做的事情。例如,假设您有一个包含两个列表框的UI,每个列表框中都有五个项目。您的操作可能是单击按钮将左侧列表中的所有内容移动到右侧列表(因此它现在有10个,左侧列表为零)。

撤消操作将所有内容移回;撤消动作只是向后移动你实际移动的五个,而离开其他动作。

答案 1 :(得分: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.   

答案 2 :(得分:5)

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

答案 3 :(得分: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
}

答案 4 :(得分:1)

我已经使用堆栈进行图像处理,其中必须在URL中指定“处理语言”。基于堆栈的表单允许您以易于解析,易于思考的形式表示操作树。

请参阅:

http://www.hackification.com/2008/10/29/stack-based-processing-part-1/

http://www.hackification.com/2008/10/29/stack-based-processing-part-2/

答案 5 :(得分:1)

在一个实际使用中,postscript生成器类具有“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类自动保存和恢复字体,但没有必要进入那些以查看堆栈的值。

答案 6 :(得分:0)

我发现堆栈在多线程应用程序中非常有用,能够以反时限方式跟踪状态......

每个线程都会在同步的共享堆栈中放置一条状态消息,并且您对所发生的事情有一种“痕迹”。

不是.NET但是......这是我的意见=)

答案 7 :(得分:0)

这是深度比较的实现,其中Stack用于跟踪当前被比较对象的路径。

C# implementation of deep/recursive object comparison in .net 3.5

我也在类似的代码类型中使用它,为特定的xml节点生成xpath语句。

答案 8 :(得分:0)

提供一个具体的例子来说明其他人正在评论的内容:要实现一个Z机器解释器,应该使用三个不同的堆栈。一个调用堆栈,以及几种不同类型的对象堆栈。 (具体要求可以找到here.)请注意,与所有这些示例一样,虽然使用堆栈并非严格必需,但它是显而易见的选择。

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

答案 9 :(得分:0)

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