在没有反向操作的情况下保存取消执行/重新执行



假设我在一个电子表格应用程序中,并执行三个操作:

  1. (打开一个新的电子表格。)
  2. 在单元格A1中键入"你好">
  3. 将单元格A1设为粗体
  4. 在单元格A2中键入"黄色">

为了取消所有操作,我可以存储以下内容:

UNDO_STACK = [
{ActiveCell: 'A1', Text: ''},      # undo for "hello"
{ActiveCell: 'A1', Bold: false},   # undo for bold
{ActiveCell: 'A2', Text: ''},      # undo for "yellow"
]

当然,可以有更多的操作,并且上面的数据结构相当简化。然而,我的问题是,像"Replace 'e' with 'l'"这样的复杂操作将如何工作?我不能保存"操作",因为它不是完全可逆的,因为大部分原始数据都丢失了。对于类似于替换操作的操作,Excel或Google工作表等应用程序是否需要保存已执行的每个替换操作?如果它是一个非常大的文件,比如Excel中的500MB文件,如果存储每个撤消操作的位置(例如,如果一半的单元格值为NULL,我想用"替换NULL),撤消操作本身就不能占用大约10MB或更多的空间,该怎么办?如何以节省内存的方式保存替换操作?

大多数编辑操作都是不可逆的。例如,将单元格A1更改为"hello"的第一个操作是不可逆的,因为它会丢失该单元格中的前一个文本。解决方案正如您在问题中所写:存储恢复原始状态所需的附加信息。在这种情况下,单元格A1在更改之前的内容是""(空字符串),因此将空字符串存储在"undo"对象中可以恢复旧状态。

同样,批量搜索和替换操作是不可逆的,因此它也需要足够的数据来恢复旧状态。一个单元格、索引和已删除子字符串的列表就足够了。如果电子表格很大,并且许多单元格受到影响,则此对象可能包含相当多的数据;原则上,您可以压缩它(例如使用像gzip这样的算法),但我怀疑许多应用程序是否能做到这一点。更可能的情况是,你只需按原样存储"撤消"对象。你在大型电子表格上进行批量编辑需要10MB内存的例子并不不切实际,但10MB内存现在也不多了。

如果"撤消"对象需要太多内存,那么您可以始终选择不存储它,在这种情况下,该操作(以及之前的任何操作)都无法撤消。在某些情况下(例如,将转换应用于整个非常大的图像),撤消操作所需的信息是原始图像的完整副本,因此您可能会收到警告,该操作将占用大量内存,无法撤消。如果要实现此操作,请确保在发生此情况时清除整个撤消堆栈。

通常,这是通过创建一个包含所有可能状态的类或对象来处理的。在您的示例中,该单元格的属性将是StringBold (Bool).。会有一些函数检测到对特定对象的更改,然后有效地将其保存到保存数据的文件或内存位置,直到需要为止。有趣的是,不同的软件使用这些状态的方式与您预期的不同。例如,Excel将存储undo操作,直到工作簿被保存,但如果Excel崩溃,它将在您恢复工作簿时尝试保留这些操作。作为参考,如果数据仅为字符串值,则一个包含约80000个单元格的excel表将使用约20Mb的数据。在高效工作时,必须记住,您可以使用SaveGenerate.

保存:保存方法将完全按照您的预期执行。它将把你的信息的每一个变化都保存在记忆中,无论设计师提出什么要求。如果你想撤消一个操作,它只会恢复到以前的保存,等等。

生成:这个有点不同,因为它是一个已知的状态更改,可以为撤消操作计算。您可以将此类型视为对象的computed property。其中一些生成的撤消操作是显而易见的,例如,存在true/false的粗体或非粗体操作。生成作品的其他原因可能更难识别,例如图像的旋转。生成将是以最有效的方式处理NULL替换的最有效的方法。您的函数仍然需要为)保存一些值,但内存中的一个字符比内存中的数百或数千个字符要好。

总而言之,我认为你使用的可生成撤销操作越多,效率就越高。除了静态数据外,几乎任何东西都有一个可以计算的逆运算,从而节省内存。从单元格A1到A1:Z26的一系列单元格的复制/粘贴可以存储为使用该精确信息的简单复制和粘贴,保存A1,然后仅将其复制到A1:Z16,这将大大提高性能,而不是将整个A1:Z2保存为一个操作。

我鼓励你看看这篇文章,它包含了一些很好的信息,可以帮助你更好地理解:https://mynameismjp.wordpress.com/2008/12/19/undo-and-redo/

最新更新