Je cherchais avec fou d'explication sur un algorithme de diff qui fonctionne et qui est efficace.
Le plus proche que j'ai eu est ce lien vers RFC 3284 (à partir de plusieurs billets de blog Eric Sink), qui décrit en termes parfaitement compréhensibles le format de données dans lequel les résultats diff sont stockés. Cependant, il n’a aucune mention de la manière dont un programme atteindrait ces résultats tout en faisant un diff.
J'essaie de chercher cela par curiosité personnelle, parce que je suis sûr qu'il doit y avoir des compromis lors de la mise en œuvre d'un algorithme de diff, qui sont assez clairs parfois quand on regarde les diffs et se demandent "pourquoi le programme de diff a-t-il choisi cela comme un changement au lieu de?"...
Où puis-je trouver une description d'un algorithme efficace qui aboutirait à la sortie de VCDIFF?
Soit dit en passant, si vous trouviez une description de l’algorithme utilisé par DiffMerge de SourceGear, ce serait encore mieux.
REMARQUE: la sous-séquence la plus longue commune ne semble pas être l'algorithme utilisé par VCDIFF, il semble que leur fonctionnement soit plus intelligent, compte tenu du format de données utilisé.
n O(ND) Algorithme de différence et ses variations est un papier fantastique que vous voudrez peut-être commencer par là. Il comprend un pseudo-code et une visualisation agréable du graphique. traversals impliqués dans faire le diff.
La section 4 de l'article introduit quelques améliorations à l'algorithme qui le rendent très efficace.
Une mise en œuvre réussie vous laissera un outil très utile dans votre boîte à outils (et probablement une excellente expérience également).
Générer le format de sortie dont vous avez besoin peut parfois être délicat, mais si vous comprenez les composants internes de l'algorithme, vous devriez être capable de générer tout ce dont vous avez besoin. Vous pouvez également introduire des heuristiques pour affecter la sortie et effectuer certains compromis.
Voici une page qui inclut un peu de documentation, code source complet , et des exemples d’un algorithme de diff utilisant les techniques de cet algorithme.
Le code source semble suivre l’algorithme de base et est facile à lire.
Il est également un peu sur la préparation de l'entrée, que vous pouvez trouver utile. Il y a une différence énorme en sortie lorsque vous différez par caractère ou par jeton (Word).
Bonne chance!
Je commencerais par regarder le code source actuel pour diff, qui GNU rend disponible =.
Pour comprendre le fonctionnement réel de ce code source, la documentation de ce paquetage fait référence aux articles qui l'ont inspiré:
L'algorithme de base est décrit dans "An O(ND) Algorithme de différence et ses variations", Eugene W. Myers, "Algorithmica", Vol. 1 n ° 2, 1986, p. 251-266. et dans "Un fichier de comparaison de programmes", Webb Miller et Eugene W. Myers, "Logiciels - Pratique et expérience", vol. 15 n ° 11, 1985, pages 1025 à 1040. L'algorithme a été découvert indépendamment comme décrit dans " Algorithmes pour la correspondance approximative de chaînes ", E. Ukkonen," Information and Control ", vol. 64, 1985, p. 100-118.
Lire les articles puis consulter le code source d’une implémentation devrait être plus que suffisant pour comprendre son fonctionnement.
Voir https://github.com/google/diff-match-patch
"Les bibliothèques Diff Match et Patch offrent des algorithmes robustes pour effectuer les opérations requises pour la synchronisation de texte brut. ... Actuellement disponible en Java, JavaScript, C++, C # et Python"
Voir aussi les wikipedia.org page Diff et - " Bram Cohen: le problème diff a été résol "
Je suis venu ici à la recherche de l'algorithme diff et ensuite j'ai créé ma propre implémentation. Désolé, je ne connais pas vcdiff.
Wikipedia : Dans une sous-séquence commune la plus longue, obtenir un résultat similaire à un diff ne constitue qu'une petite étape: si un élément est absent de la sous-séquence mais présent dans l'original, il doit avoir été supprimé. (Les marques '-' ci-dessous.) S'il est absent dans la sous-séquence mais présent dans la deuxième séquence, il doit avoir été ajouté. (Les marques '+'.)
Belle animation de la algorithme LCS ici .
Lien vers un rapide implémentation de LCS Ruby ici .
Ma lente et simple adaptation Ruby est ci-dessous.
def lcs(xs, ys)
if xs.count > 0 and ys.count > 0
xe, *xb = xs
ye, *yb = ys
if xe == ye
return [xe] + lcs(xb, yb)
end
a = lcs(xs, yb)
b = lcs(xb, ys)
return (a.length > b.length) ? a : b
end
return []
end
def find_diffs(original, modified, subsequence)
result = []
while subsequence.length > 0
sfirst, *subsequence = subsequence
while modified.length > 0
mfirst, *modified = modified
break if mfirst == sfirst
result << "+#{mfirst}"
end
while original.length > 0
ofirst, *original = original
break if ofirst == sfirst
result << "-#{ofirst}"
end
result << "#{sfirst}"
end
while modified.length > 0
mfirst, *modified = modified
result << "+#{mfirst}"
end
while original.length > 0
ofirst, *original = original
result << "-#{ofirst}"
end
return result
end
def pretty_diff(original, modified)
subsequence = lcs(modified, original)
diffs = find_diffs(original, modified, subsequence)
puts 'ORIG [' + original.join(', ') + ']'
puts 'MODIFIED [' + modified.join(', ') + ']'
puts 'LCS [' + subsequence.join(', ') + ']'
puts 'DIFFS [' + diffs.join(', ') + ']'
end
pretty_diff("human".scan(/./), "chimpanzee".scan(/./))
# ORIG [h, u, m, a, n]
# MODIFIED [c, h, i, m, p, a, n, z, e, e]
# LCS [h, m, a, n]
# DIFFS [+c, h, +i, -u, m, +p, a, n, +z, +e, +e]
Basé sur le lien donné par Emmelaich, il existe également un excellent compte rendu de Diff Strategies sur site Web de Neil Fraser (un des auteurs de la bibliothèque) .
Il couvre les stratégies de base et progresse vers la fin de l'article vers l'algorithme de Myer et une théorie des graphes.