【问题标题】:Delphi - TStack Capacity confusionDelphi - TStack 容量混乱
【发布时间】:2017-01-14 23:23:40
【问题描述】:

我一直在使用 TStack 尝试在我的程序中实现一个简单的撤消/重做功能。这背后的想法是,当一个动作被执行时,程序的当前状态被保存——即被压入堆栈。当用户单击撤消时,程序的最后一个状态被重新加载 - 即从堆栈中弹出。

这个想法的缺陷是堆栈不能永远增长,这意味着在达到容量值后,应该删除最旧的项目(堆栈底部的那些),因为新项目被推到顶部。

Delphi 中的 TStack 对象包含一个容量属性,我假设它会自动执行此“清理”,但是当我使堆栈过载(例如,将 11 个项目推入一个容量为 10 的项目)时,容量会更新以容纳更多项目。

谁能就在这种情况下如何更有效地使用 TStack 向我提供任何建议?我知道另一种方法是使用数组结构,但我喜欢使用堆栈的预期易用性。

问候

【问题讨论】:

  • 也许更容易使用队列和队列中最后添加的项目所在的指示符。这样你就可以选择重做。
  • 堆栈不适合你。放弃。这是错误的数据结构。您可能正在寻找双端队列。或者只是使用固定长度的数组创建自己的。当数组已满时使用循环索引。
  • 抱歉,Capacity 属性是,正如你所发现的,自动增加。他与所有TOrderedList 派生集合对象相同。也没有设置强迫它不增长。您可以使用OnNotify 事件来检查是否达到了您自己的限制,如果达到,请根据需要更改内容。
  • @TomBrunberg 堆栈不适合从底部移除,那么您将如何执行限制?
  • @DavidHeffernan 你是对的。最好按照您的建议在数组的基础上从头开始构建它。

标签: arrays delphi stack undo redo


【解决方案1】:

实际上,TStack 由动态数组提供支持。
如果你真的想要,你可以滥用这个事实让堆栈从底部删除项目,但现在你将与堆栈作斗争。

我认为使用循环列表是一个更好的主意。

一个简单的设计可能会像这样工作。

type
  TCircularList<T> = class(TObject);
  private
    FStorage: TList<T>;
    FCapacity: cardinal;  //duplication for performance reasons
    FCurrentIndex: cardinal;
  protected
    function GetItem(index: cardinal): T;
    procedure SetItem(index: cardinal; const Value: T);
  public
    constructor Create(Size: cardinal = 10);
    destructor Destroy; override;
    procedure Add(const Item: T);
    property Items[index: cardinal]: T read GetItem write SetItem; default;
  end;

constructor TCircularList<T>.Create(Size: cardinal = 10);
begin
  inherited Create;
  Assert(Size >= 2);
  FStorage:= TList<T>.Create;
  FCapacity:= Size;
  FStorage.Capacity:= Size;
end;

destructor TCircularList<T>.Destroy;
begin
  FStorage.Free;
  inherited;
end;

procedure TCircularList<T>.Add(const Item: T);
begin
  FCurrentIndex:= (FCurrentIndex + 1) mod FCapacity;
  FStorage[FCurrentIndex]:= Item;
end;

function TCircularList<T>.GetItem(index: cardinal): T;
var
  cIndex: cardinal;
begin
  cIndex:= Index mod FCapacity;
  Result:= FStorage[index]; 
end;

procedure TCircularList<T>.SetItem(index: cardinal; const Value: T);
var
  cIndex: cardinal;
begin
  cIndex:= index mod FCapacity;
  FStorage[index]:= Value;
end;

显然,您需要更多方法,例如 last 和 delete 方法,但我将其留给您,您应该可以从这里推断。

可用性说明
我不得不说,从用户体验的角度来看,我认为撤消/重做功能的想法很糟糕。
为什么没有快照列表,您可以及时前后移动,就像您在磁盘上保存许多备份文件时的情况一样。
撤消功能要求您准确地记住您在最后 x 步中所做的事情,这不是很好地扩展。
我也不明白为什么必须有限制,为什么不允许内存/磁盘空间允许的尽可能多的撤消/快照?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-13
    • 1970-01-01
    相关资源
    最近更新 更多