【问题标题】:printing Diff from Longest Common Subsequence从最长公共子序列打印 Diff
【发布时间】:2015-06-14 23:19:01
【问题描述】:

所以我一直在为 Perl 和 Python 做一些练习题(在两者之间进行选择),我遇到了一个问题,我需要像 Github 一样制作自己的差异算法。 我已经知道最长公共子序列问题是解决方案的重要组成部分。我使用了wikipedia page for LCS 作为参考,但我仍然无法找出差异部分。

我也意识到 CPAN 上已经有像 Algorithm:Diff 这样的模块,但这主要是为了练习,那些感觉像是在作弊。

我找到了算法的 python/伪代码版本,但我打算用 Perl 似乎没有的多维数组来做。

现在我可以在 Perl 中成功获得最长公共子序列长度了。

基本上我能想到的伪代码(几乎类似于 python,但应该用于 Perl)是这样的:

function lengthOfLCS(string1, string2){
    if length(string1) == 0 or length(string2) == 0:
         return 0
    else if string1[0] eq string2[0]: 
         return 1+ lengthOfLCS(stringA[1:], stringB[1:])
    return max(lengthOfLCS(string1, string2[1:], lengthOfLCS(string1[1:], string2))

我还没有实现它,但我认为这基本上就是我可以计算两个字符串的 LCS 长度的方法吗?

输出明智,它应该针对“人类”和“黑猩猩”(LCS = HMAN)返回 4

所以我要问的是,从现在开始,我如何才能使用 Perl 打印 Diffs?我知道,我应该返回一个列表/数组,而不是只返回 LCS 的长度,这可以通过在 LCS 函数中返回一个多维列表,然后稍后在单独的 diff 函数中处理它来实现.

我对 Perl 有点陌生,所以任何指针/提示将不胜感激。 谢谢。

【问题讨论】:

  • 这是answered 维基百科的详细信息
  • CPAN 上还有 Algorithm::MLCSAlgorithm::DiffAlgorithm::LCSAlgorithm::NeedlemanWunsch
  • 您好,感谢您的建议。 @ikegami 实际上我确实参考了 wiki 页面,在这里忘记提及了。但是,我还是不明白 Diff 位,wikipedia 中解释的代码大多使用多维数组,而 Perl 没有?你能给我推荐一个 Perl 替代品吗?还有关于 CPAN,我主要是为了练习而不是为了实际使用(纯粹是教育),所以我不想使用这些。无论如何,谢谢,我可能会查看源代码以尝试找出它们。
  • 它有数组(引用)数组。一样。 $a[5][7]

标签: python perl diff lcs


【解决方案1】:

您可以在 Perl 中使用我的 LCS 参考实现,它需要两个数组引用作为输入,并返回一个包含两个元素数组的数组,其中包含匹配元素的索引。

use LCS;
my $lcs = LCS->LCS( [qw(a b)], [qw(a b b)] );
# $lcs now contains an arrayref of matching positions
# same as
$lcs = [
  [ 0, 0 ],
  [ 1, 2 ]
];

LCS 使用传统算法并迭代读取 LCS(请参阅我在 wollmers-perl.blogspot.de 上的博客文章 Loopify Recursions),即不是递归的(大多数示例代码使用递归,在 Perl 中不能很好地扩展)。因此,如果您想从代码中学习,请查看 subs LCS() 和 _lcs()。

如果你想要差异,即编辑脚本,你可以从 LCS 数组中重建它。

lcs2align() 方法几乎可以做到这一点。

use Data::Dumper;
use LCS;
print Dumper(
  LCS->lcs2align(
    [qw(a   b)],
    [qw(a b b)],
    LCS->LCS([qw(a b)],[qw(a b b)])
  )
);
# prints
$VAR1 = [
          [
            'a',
            'a'
          ],
          [
            '',
            'b'
          ],
          [
            'b',
            'b'
          ]
];

sdiff() (see Algorithm::Diff) 格式的差异现在看起来像:

[
  [ 'u', 'a', 'a'  ],
  [ '+', '',  'b'  ],
  [ 'u', 'b', 'b'  ],
]

如何从对齐中获取编辑脚本应该很简单,留作练习。

如果您想要更快的实现,您可以使用 LCS::Tiny,或最快的纯 Perl 实现 LCS::BV,或用于更大规模的最快算法 Algorithm::Diff::XS(请参阅我的博客文章 Tuning Algorithm::Diff,网址为wollmers-perl.blogspot.de)

请记住,基于 LCS 的编辑脚本不会自动提供 SES(最短编辑脚本)。 LCS 基于只允许插入和删除(简单编辑距离)的编辑操作。 SES 算法通常最小化 Levenshtein 距离(插入、删除和不匹配)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    • 2021-11-04
    • 2023-04-02
    • 2011-03-01
    相关资源
    最近更新 更多