上下文敏感的diff实现

时间:2013-12-23 21:54:38

标签: html algorithm vba access-vba diff

摘要和基本问题

使用MS Access 2010和VBA(叹息..)

我正在尝试实现一个专门的Diff函数,它能够根据更改的内容以不同的方式输出更改列表。我需要能够生成一份简明的更改列表,以便为我们的记录提交。

我想使用诸如<span class="references">These are references 1, 6</span>之类的html标签之类的内容,以便我可以使用代码查看更改并自定义更改文本的输出方式。或其他任何事情来完成我的任务。

我认为这是一种提供自定义输出的可扩展方式的方法,并可能将事物移动到更强大的平台并实际使用html / css。

是否有人知道类似的项目可能会指出我正确的方向?

我的任务

我有一个包含工作操作指令表的访问数据库 - 通常是200-300个操作,其中许多操作从一个修订版更改为另一个修订版。我目前已经实现了一个遍历表的函数,查找已更改的指令并对它们进行比较。

请注意,每个操作指令通常是几个句子,最后有几行文档引用。

我的算法基于"An O(ND) Difference Algorithm and Its Variations",效果很好。

Access支持“Rich”文本,它只是美化简单的html,因此我可以轻松生成带有格式化添加和删除的全文,即添加<font color = "red"><strong><i>This text has been removed</i></strong></font>等标记。 Diff过程的主要输出是操作的全文,其中包括彼此内联的未更改,已删除和插入的文本。 diff过程添加了<del><ins>标签,稍后将替换为格式化文本(结果类似于堆栈交换编辑的更改视图)。

然而,正如我所说,我需要以人类可读格式列出的更改。事实证明这很困难,因为许多变化产生了模糊性。

例如:如果某种化学品从“A类”变为“C类”,则容易生成的更改文本是“将'A'更改为'C'”,这对于有人审查这些变化。更常见的是文档参考结尾:将SOP 3添加到列表中,例如“SOP 1,2,3”,生成文本“添加'3'”。显然也没用。

什么是最有用的是指定为“SOP”文本的文本的自定义输出,以便输出为“添加对SOP 3的引用”。

我从以下解决方案开始:

将单词组合在一起,例如将“SOP 1,2,3”等文本作为一个标记进行比较。这将生成文本“将'SOP 1,2'改为'SOP 1,2,3'。当存在大型列表并且您试图确定实际更改的内容时,这会变得混乱。

我现在在哪里

我现在正在尝试在运行diff算法之前添加额外的html标签。例如,我将通过“预处理器”运行文本,将“SOP 1,2”转换为SOP 1,2

一旦Diff过程返回完整的更改文本,我会浏览它,注意文本的当前“类”,当有<del><ins>时,我会在标记之间捕获文本并使用在课程上SELECT CASE阻止每个更改。

这实际上在大多数情况下都可以正常工作,但是我必须解决许多问题,例如添加Diff决定最短路径是删除某些开放标记并插入其他开放标记。这会创建一个场景,即有两个<span>标记,但只有一个</span>标记。

最终的问题

我正在寻找建议,要么继续我已经开始的方向,要么在将更多时间投入到次优解决方案之前尝试不同的事情。

提前全部谢谢。

另请注意:

典型运行的时间大约是1.5到2.5秒,我尝试了更多花哨的东西和一堆debug.prints。因此,通过一两个额外的通行证不会成为杀手。

3 个答案:

答案 0 :(得分:4)

很明显,报告您所拥有的结构的最小变化的差异并不是您想要的;你想报告一些背景。

要做到这一点,您必须确定要报告的上下文,以便您可以决定哪个部分很有趣。你勾勒出一个想法,你将你的结构的某些元素融合在一起(例如,'SOP''1''2'加入'SOP 1 2'),但在我看来,这似乎是错误的方式。它正在做的是改变最小结构元素的大小,而不是报告更好的上下文。

虽然我不确定这是正确的方法,但我会尝试使用语法来表征你的结构,例如BNF。例如,您可能会有一些语法规则:

 action = 'SOP' sop_item_list ;
 sop_item_list = NATURAL ;
 sop_item_list = sop_item_list ',' NATURAL ;

现在,一个实际的SOP项可以被描述为一个抽象语法树(显示嵌套子项,可以通过常量转换为子树):

 t1: action('SOP',sop_item_list(sop_item_list(NATURAL[1]),',',NATURAL[2]))

您仍然希望使用类似于您建议的动态编程算法来计算差异,但现在您需要最小的树增量。做得对(我的公司为常规计算机语言的语法制作工具,你可以找到公开的树差异算法),你可以获得如下的delta:

  replace t1[2] with op_item_list(sop_item_list(sop_item_list(NATURAL[1]),',',NATURAL[2]),',',NATURAL[3]

本质上是你通过粘合(SOP,1,2)到单个项目中得到的东西,但没有你个人决定这样做的外部特设。

我认为,真正的价值在于你可以使用树t1来报告上下文。特别是,从树的根开始,并打印子树的摘要(显然,您不希望打印完整的子树,因为这样可以返回完整的原始文本)。 通过将子树打印到1或2级深度并删除任何深度(例如,将列表表示为“...”并将单个子树表示为“_”),您可以打印如下内容:

replace 'SOP' ... with 'SOP',...,3

我认为这是你在具体例子中所寻找的。

不,这不是算法;它是一个想法的草图。事实上,我们有计算有用增量的树 - 增量算法,以及总结的想法(从LISP调试器中,坦率地说),这可能会推广到有用的东西,或者至少带你走向新的方向。

根据AST提供答案,还应该根据需要相对容易地生成HTML。 (人们一直使用XML,而XML基本上是树表示。)

答案 1 :(得分:3)

如果你决定继续你已经取得的成就(IMO你已经很远了),你可以考虑做两个差异步骤。

  

将单词组合在一起,例如将“SOP 1,2,3”等文本作为一个标记进行比较。

这是一个好的开始;你已经设法让用户清楚了解上下文。

  

这将生成文本“将'SOP 1,2'更改为'SOP 1,2,3'”。当有一个大型列表并且您正在尝试确定实际更改的内容时,这会变得混乱。

如何在发现的标记上进行另一次差异传递(即将'SOP 1,2'与'SOP 1,2,3'比较),这次没有分组,生成其他信息?这将使完整的输出像这样:

  

将'SOP 1,2'改为'SOP 1,2,3'

     

更改详情:添加'3'

文字有点神秘,所以你可能想在那里做一些改进。我还建议在第一行截断冗长的标记('SOP 1,2,3,...'),因为第二行应该已经提供了足够的细节。

我不确定第二次通过对性能的影响;在一个有很多变化的大文本中,你可能会遇到很多往返功能的往返。您可以通过将第一遍中的更改累积到一个“更改文档”中进行优化,在其中运行第二遍,然后将结果拼接在一起。

HTH。

答案 2 :(得分:3)

尝试考虑Prolog风格的重写规则,将您的指令转换为规范形式,这将导致diff算法生成您需要的内容。您指定的问题将通过此规则解决:

SOP i1, i2, ... iN  ->  SOP j1, SOP j2, ... SOP jN  where j = sorted(i)

换句话说,在以下整数的排序列表上“分配”SOP。这将欺骗diff算法提供一个完全合格的变更报告“Add SOP 3。”

通过在输入中搜索左侧的匹配并替换为相应的右侧来应用规则。

您可能已经这样做了,但如果输入被标记化,您将获得更多常识分析:“SOP”应该被视为diff算法的单个“字符”。如果空格很重要或被忽略,则空格可以缩减为空格和换行符令。

您可以在角色级别执行另一种差异来测试令牌的“模糊”相等性,以便在匹配左侧时考虑到印刷错误。 “SIP”和“SOP”将被视为“匹配”,因为它们的编辑距离仅为1(而且我和O在QUERTY键盘上只有一个键!)。

如果你考虑输出中你现在得到的所有怪癖并且可以将每一个作为一个重写规则来纠正,将输入转换为diff算法产生你需要的形式,那么剩下的就是实现重写系统。以一般有效的方式执行此操作,以便更改和添加规则不涉及大量的临时编码是一个难题,但已经研究过。有趣的是,@ Ira Baxter提到了lisp,因为它擅长于此类工具。

Ira对语法树的建议自然属于重写规则方法。例如,假设SOP有章节和段落:

SOP 1 section 3, paras 2,1,3

是一个应该重写为

的层次结构
SOP 1 section 3 para 1, SOP 1 section 3 para 2, SOP 1 section 3 para 3

重写规则

paras i1, i2, ... iN  ->  para j1, para j2, ... para jN  where j = sorted(i)
section K para i1, ... para iN  ->s section K para j1, ... section K para j1
SOP section K para i1, ... section K para i1 -> SOP section K para j1, ... SOP section K para j1 

当在三次通过中应用时将产生类似“SOP 1第3节,第4段被添加”的结果。

虽然有很多策略来实现规则和重写,包括将每个规则编码为VB(argh ...)中的过程,但还有其他方法。 Prolog是尽可能普遍地做到这一点的重大尝试。 There is a free implementation.还有其他人。我使用TXL来完成有用的重写。 TXL的唯一问题是它假设你对输入有一个相当严格的语法,这听起来不像你的问题。

如果你在当前输出中发布更多关于怪癖的例子,我可以详细介绍重写规则。