是否存在保留线所有权的差异算法

时间:2009-10-05 17:18:59

标签: diff algorithm

我的目标是提供一个脚本来跟踪添加一行的点,即使该行随后被修改或移动(这两者都混淆了传统的vcs'责备'脚本。我做了一些小的背景研究(见底部)但没有找到任何有用的东西。我有一个关于如何进行的概念,但运行时会很糟糕(涉及到一个因子)。

这两个缺失的功能是跟踪编辑就地线,与该线的删除和添加分开,并跟踪移动的所有功能,因此它们处于不同的状态。对于那些经历过差异但不熟悉术语的人来说,子序列是+-行的连续组,其类型为delete(所有-), add(所有+)或replace(组合)。我需要更多关于移动和edit-in-place行的信息,在c2: DiffAlgorithm的条目中含糊其词地提到(段落以“我最喜欢的模式”开头)。有谁知道那是什么? (似乎是基于Tichy,见底部。)


以下是有关这两个缺失功能​​的更多信息:

  1. 没有关于线路变化的概念,(第四种类型,如edit-in-place)。在这个大块中,'bc'的父级是'b'但是'd'是新的并且不是'b'的后代:
  2.  a
    -b
    +bc
    +d
    

    如果编辑的位置相同(仅markup_instraline_changes的扩展版本,但比较旧行和新行的所有大小相等的子集上的编辑距离),解决方法并不太复杂。

    1. 没有保留线路所有权的“移动”代码的概念,例如:虽然它的位置发生了变化,但这种差异不应该改变“线”的所有权。
    2.  a
      -line
       c
      +line
      

      这可以用相同的方式处理,但运行时间要差得多(而不是只检查标记为'replace'的单个块,你需要检查所有添加的所有删除行之间的Levenshtein距离)以及可能的误报(有些,就像只有空格的行一样,与我的问题无关)。

      研究我已经完成了:阅读gestalt pattern matching(Ratcliff和Obershelp,用于Python的difflib)和An O(ND) Difference Algorithm and its Variations(EW Myers)。

      在发布问题后,根据Walter Tichy一年后的论文The string-to-string correction problem with block moves

      ,我发现Tichy84的引用似乎是on RCS(我还没有读过)

2 个答案:

答案 0 :(得分:1)

您似乎对原点跟踪感兴趣,这是追踪线路来源的问题。

理想情况下,您可以让编辑器记住事情的编辑方式,并将编辑内容与文本存储在存储库中,从而轻松解决问题,但我们这些软件工程师似乎都不够聪明,无法实现这一点想法。

作为一个弱替代品,人们可以从存储库中查看一系列源代码修订,并重建“似是而非的”变化历史。这就是你似乎通过提出使用“diff”来做的事情。正如你所指出的,diff不理解“移动”或“复制”的想法。

SD Smart Differencer工具通过根据文本的语言解析文本,发现代码结构,并根据编程语言结构(标识符,表达式,语句,块,计算最小Levensthein差异)来比较源文本。类,...)和抽象编辑操作符“插入”,“删除”,“复制”,“移动”和“重命名范围内的标识符”。它们会产生类似差异的输出,因为它们会告诉你行/列,所以会更加丰富一些 - >具有不同编辑操作的行/列。

显然,在跟踪特定行(以及特定语言结构)方面,“移动”和“复制”编辑是您最感兴趣的。我们的经验是代码也经历了大量的复制和编辑,我怀疑这不会给你带来惊喜。

这些工具都是Beta版,目前可用于COBOL,Java和C#。管道中还有很多其他语言,因为SmartDifferencer是建立在语言参数化基础架构DMS Software Reengineering Toolkit之上的,它具有相当多的现有的,强大的语言语法。

答案 1 :(得分:1)

我认为,当它仍然是一些先前写过的行的后代时,可以完成的行编辑量的想法是非常主观的,并且基于上下文,这两者都是计算机无法使用的。您必须在程序中的行上指定某种可配置的最小相似度我认为...另一个问题是完全可以完全独立地写两条相同的行(例如递增某些变量的值) ,这将是一个非常普遍的事情,所以你想要的算法不会经常提供关于一行的真实或有用的信息。

我想为此提出一个算法(顺便提一下,这会产生大量有希望的明显假设),所以这里有:

Convert both texts to lists of lines
Copy the lists and Strip all whitespace from inside of each line
Delete blank lines from both lists
Repeat
   Do a Levenshtein distance from the old to new lists ...
    ... keeping all intermediate data
   Find all lines in the new text that were matched with old lines
      Mark the line in both new/old original lists as having been matched
      Delete the line from the new text (the copy)
   Optional: If some matched lines are in a contiguous sequence ...
    ... in either original text assign them to a grouping as well!
Until there is nothing left but unmatchable lines in the new text
Group together sequences of unmatched lines in both old and new texts ...
 ... which are contiguous in the original text
   Attribute each with the line match before and after
Run through all groups in old text
   If any match before and after attributes with new text groups for each
    //If they are inside the same area basically
      Concatenate all the lines in both groups (separately and in order)
         Include a character to represent where the line breaks are
      Repeat
         Do a Levenshtein distance on these concatenations
         If there are any significantly similar subsequences found
          //I can't really define this but basically a high proportion
          //of matches throughout all lines involved on both sides
            For each matched subsequence
               Find suitable newline spots to delimit the subsequence
               Mark these lines matched in the original text
                //Warning splitting+merging of lines possible
                //No 1-to-1 correspondence of lines here!
               Delete the subsequence from the new text group concat
               Delete also from the new text working list of lines
      Until there are no significantly similar subsequences found
Optional: Regroup based on remaining unmatched lines and repeat last step
 //Not sure if there's any point in trying that at the moment
Concatenate the ENTIRE list of whitespaced-removed lines in the old text
Concatenate the lines in new text also (should only be unmatched ones left)
 //Newline character added in both cases
Repeat
   Do Levenshtein distance on these concatenations
   Match similar subsequences in the same way as earlier on
    //Don't need to worry deleting from list of new lines any more though
    //Similarity criteria should be a fair bit stricter here to avoid
    // spurious matchings.  Already matched lines in old text might have 
    // even higher strictness, since all of copy/edit/move would be rare
While you still have matchings

//Anything left unmatched in the old text is deleted stuff
//Anything left unmatched in the new text is newly written by the author
Print out some output to show all the comparing results!

好吧,希望你能看到我对这个完全未经测试的算法的基本知识。首先找到明显的匹配,然后逐渐减少大小的块,然后比较可能相似的东西,然后查找其他类似的东西,但是修改和移动的东西:可能恰好相似。

好吧,如果您尝试实现这一点,请告诉我它是如何工作的,您更改了哪些细节,以及您对所涉及的各种变量进行了哪些分配...我希望会有一些测试用例可以使用它和其他人一样,由于一些大规模的监督而非常失败。我们的想法是,在你进入效率低下的最终循环之前,大多数东西都会匹配,实际上是之前的那个