Edit File by line
/home/barbar84/public_h.../wp-conte.../plugins/sujqvwi/ShExBy/shex_roo.../usr/lib64/python3....
File: difflib.py
"""
[0] Fix | Delete
Module difflib -- helpers for computing deltas between objects.
[1] Fix | Delete
[2] Fix | Delete
Function get_close_matches(word, possibilities, n=3, cutoff=0.6):
[3] Fix | Delete
Use SequenceMatcher to return list of the best "good enough" matches.
[4] Fix | Delete
[5] Fix | Delete
Function context_diff(a, b):
[6] Fix | Delete
For two lists of strings, return a delta in context diff format.
[7] Fix | Delete
[8] Fix | Delete
Function ndiff(a, b):
[9] Fix | Delete
Return a delta: the difference between `a` and `b` (lists of strings).
[10] Fix | Delete
[11] Fix | Delete
Function restore(delta, which):
[12] Fix | Delete
Return one of the two sequences that generated an ndiff delta.
[13] Fix | Delete
[14] Fix | Delete
Function unified_diff(a, b):
[15] Fix | Delete
For two lists of strings, return a delta in unified diff format.
[16] Fix | Delete
[17] Fix | Delete
Class SequenceMatcher:
[18] Fix | Delete
A flexible class for comparing pairs of sequences of any type.
[19] Fix | Delete
[20] Fix | Delete
Class Differ:
[21] Fix | Delete
For producing human-readable deltas from sequences of lines of text.
[22] Fix | Delete
[23] Fix | Delete
Class HtmlDiff:
[24] Fix | Delete
For producing HTML side by side comparison with change highlights.
[25] Fix | Delete
"""
[26] Fix | Delete
[27] Fix | Delete
__all__ = ['get_close_matches', 'ndiff', 'restore', 'SequenceMatcher',
[28] Fix | Delete
'Differ','IS_CHARACTER_JUNK', 'IS_LINE_JUNK', 'context_diff',
[29] Fix | Delete
'unified_diff', 'diff_bytes', 'HtmlDiff', 'Match']
[30] Fix | Delete
[31] Fix | Delete
from heapq import nlargest as _nlargest
[32] Fix | Delete
from collections import namedtuple as _namedtuple
[33] Fix | Delete
[34] Fix | Delete
Match = _namedtuple('Match', 'a b size')
[35] Fix | Delete
[36] Fix | Delete
def _calculate_ratio(matches, length):
[37] Fix | Delete
if length:
[38] Fix | Delete
return 2.0 * matches / length
[39] Fix | Delete
return 1.0
[40] Fix | Delete
[41] Fix | Delete
class SequenceMatcher:
[42] Fix | Delete
[43] Fix | Delete
"""
[44] Fix | Delete
SequenceMatcher is a flexible class for comparing pairs of sequences of
[45] Fix | Delete
any type, so long as the sequence elements are hashable. The basic
[46] Fix | Delete
algorithm predates, and is a little fancier than, an algorithm
[47] Fix | Delete
published in the late 1980's by Ratcliff and Obershelp under the
[48] Fix | Delete
hyperbolic name "gestalt pattern matching". The basic idea is to find
[49] Fix | Delete
the longest contiguous matching subsequence that contains no "junk"
[50] Fix | Delete
elements (R-O doesn't address junk). The same idea is then applied
[51] Fix | Delete
recursively to the pieces of the sequences to the left and to the right
[52] Fix | Delete
of the matching subsequence. This does not yield minimal edit
[53] Fix | Delete
sequences, but does tend to yield matches that "look right" to people.
[54] Fix | Delete
[55] Fix | Delete
SequenceMatcher tries to compute a "human-friendly diff" between two
[56] Fix | Delete
sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the
[57] Fix | Delete
longest *contiguous* & junk-free matching subsequence. That's what
[58] Fix | Delete
catches peoples' eyes. The Windows(tm) windiff has another interesting
[59] Fix | Delete
notion, pairing up elements that appear uniquely in each sequence.
[60] Fix | Delete
That, and the method here, appear to yield more intuitive difference
[61] Fix | Delete
reports than does diff. This method appears to be the least vulnerable
[62] Fix | Delete
to synching up on blocks of "junk lines", though (like blank lines in
[63] Fix | Delete
ordinary text files, or maybe "<P>" lines in HTML files). That may be
[64] Fix | Delete
because this is the only method of the 3 that has a *concept* of
[65] Fix | Delete
"junk" <wink>.
[66] Fix | Delete
[67] Fix | Delete
Example, comparing two strings, and considering blanks to be "junk":
[68] Fix | Delete
[69] Fix | Delete
>>> s = SequenceMatcher(lambda x: x == " ",
[70] Fix | Delete
... "private Thread currentThread;",
[71] Fix | Delete
... "private volatile Thread currentThread;")
[72] Fix | Delete
>>>
[73] Fix | Delete
[74] Fix | Delete
.ratio() returns a float in [0, 1], measuring the "similarity" of the
[75] Fix | Delete
sequences. As a rule of thumb, a .ratio() value over 0.6 means the
[76] Fix | Delete
sequences are close matches:
[77] Fix | Delete
[78] Fix | Delete
>>> print(round(s.ratio(), 3))
[79] Fix | Delete
0.866
[80] Fix | Delete
>>>
[81] Fix | Delete
[82] Fix | Delete
If you're only interested in where the sequences match,
[83] Fix | Delete
.get_matching_blocks() is handy:
[84] Fix | Delete
[85] Fix | Delete
>>> for block in s.get_matching_blocks():
[86] Fix | Delete
... print("a[%d] and b[%d] match for %d elements" % block)
[87] Fix | Delete
a[0] and b[0] match for 8 elements
[88] Fix | Delete
a[8] and b[17] match for 21 elements
[89] Fix | Delete
a[29] and b[38] match for 0 elements
[90] Fix | Delete
[91] Fix | Delete
Note that the last tuple returned by .get_matching_blocks() is always a
[92] Fix | Delete
dummy, (len(a), len(b), 0), and this is the only case in which the last
[93] Fix | Delete
tuple element (number of elements matched) is 0.
[94] Fix | Delete
[95] Fix | Delete
If you want to know how to change the first sequence into the second,
[96] Fix | Delete
use .get_opcodes():
[97] Fix | Delete
[98] Fix | Delete
>>> for opcode in s.get_opcodes():
[99] Fix | Delete
... print("%6s a[%d:%d] b[%d:%d]" % opcode)
[100] Fix | Delete
equal a[0:8] b[0:8]
[101] Fix | Delete
insert a[8:8] b[8:17]
[102] Fix | Delete
equal a[8:29] b[17:38]
[103] Fix | Delete
[104] Fix | Delete
See the Differ class for a fancy human-friendly file differencer, which
[105] Fix | Delete
uses SequenceMatcher both to compare sequences of lines, and to compare
[106] Fix | Delete
sequences of characters within similar (near-matching) lines.
[107] Fix | Delete
[108] Fix | Delete
See also function get_close_matches() in this module, which shows how
[109] Fix | Delete
simple code building on SequenceMatcher can be used to do useful work.
[110] Fix | Delete
[111] Fix | Delete
Timing: Basic R-O is cubic time worst case and quadratic time expected
[112] Fix | Delete
case. SequenceMatcher is quadratic time for the worst case and has
[113] Fix | Delete
expected-case behavior dependent in a complicated way on how many
[114] Fix | Delete
elements the sequences have in common; best case time is linear.
[115] Fix | Delete
[116] Fix | Delete
Methods:
[117] Fix | Delete
[118] Fix | Delete
__init__(isjunk=None, a='', b='')
[119] Fix | Delete
Construct a SequenceMatcher.
[120] Fix | Delete
[121] Fix | Delete
set_seqs(a, b)
[122] Fix | Delete
Set the two sequences to be compared.
[123] Fix | Delete
[124] Fix | Delete
set_seq1(a)
[125] Fix | Delete
Set the first sequence to be compared.
[126] Fix | Delete
[127] Fix | Delete
set_seq2(b)
[128] Fix | Delete
Set the second sequence to be compared.
[129] Fix | Delete
[130] Fix | Delete
find_longest_match(alo, ahi, blo, bhi)
[131] Fix | Delete
Find longest matching block in a[alo:ahi] and b[blo:bhi].
[132] Fix | Delete
[133] Fix | Delete
get_matching_blocks()
[134] Fix | Delete
Return list of triples describing matching subsequences.
[135] Fix | Delete
[136] Fix | Delete
get_opcodes()
[137] Fix | Delete
Return list of 5-tuples describing how to turn a into b.
[138] Fix | Delete
[139] Fix | Delete
ratio()
[140] Fix | Delete
Return a measure of the sequences' similarity (float in [0,1]).
[141] Fix | Delete
[142] Fix | Delete
quick_ratio()
[143] Fix | Delete
Return an upper bound on .ratio() relatively quickly.
[144] Fix | Delete
[145] Fix | Delete
real_quick_ratio()
[146] Fix | Delete
Return an upper bound on ratio() very quickly.
[147] Fix | Delete
"""
[148] Fix | Delete
[149] Fix | Delete
def __init__(self, isjunk=None, a='', b='', autojunk=True):
[150] Fix | Delete
"""Construct a SequenceMatcher.
[151] Fix | Delete
[152] Fix | Delete
Optional arg isjunk is None (the default), or a one-argument
[153] Fix | Delete
function that takes a sequence element and returns true iff the
[154] Fix | Delete
element is junk. None is equivalent to passing "lambda x: 0", i.e.
[155] Fix | Delete
no elements are considered to be junk. For example, pass
[156] Fix | Delete
lambda x: x in " \\t"
[157] Fix | Delete
if you're comparing lines as sequences of characters, and don't
[158] Fix | Delete
want to synch up on blanks or hard tabs.
[159] Fix | Delete
[160] Fix | Delete
Optional arg a is the first of two sequences to be compared. By
[161] Fix | Delete
default, an empty string. The elements of a must be hashable. See
[162] Fix | Delete
also .set_seqs() and .set_seq1().
[163] Fix | Delete
[164] Fix | Delete
Optional arg b is the second of two sequences to be compared. By
[165] Fix | Delete
default, an empty string. The elements of b must be hashable. See
[166] Fix | Delete
also .set_seqs() and .set_seq2().
[167] Fix | Delete
[168] Fix | Delete
Optional arg autojunk should be set to False to disable the
[169] Fix | Delete
"automatic junk heuristic" that treats popular elements as junk
[170] Fix | Delete
(see module documentation for more information).
[171] Fix | Delete
"""
[172] Fix | Delete
[173] Fix | Delete
# Members:
[174] Fix | Delete
# a
[175] Fix | Delete
# first sequence
[176] Fix | Delete
# b
[177] Fix | Delete
# second sequence; differences are computed as "what do
[178] Fix | Delete
# we need to do to 'a' to change it into 'b'?"
[179] Fix | Delete
# b2j
[180] Fix | Delete
# for x in b, b2j[x] is a list of the indices (into b)
[181] Fix | Delete
# at which x appears; junk and popular elements do not appear
[182] Fix | Delete
# fullbcount
[183] Fix | Delete
# for x in b, fullbcount[x] == the number of times x
[184] Fix | Delete
# appears in b; only materialized if really needed (used
[185] Fix | Delete
# only for computing quick_ratio())
[186] Fix | Delete
# matching_blocks
[187] Fix | Delete
# a list of (i, j, k) triples, where a[i:i+k] == b[j:j+k];
[188] Fix | Delete
# ascending & non-overlapping in i and in j; terminated by
[189] Fix | Delete
# a dummy (len(a), len(b), 0) sentinel
[190] Fix | Delete
# opcodes
[191] Fix | Delete
# a list of (tag, i1, i2, j1, j2) tuples, where tag is
[192] Fix | Delete
# one of
[193] Fix | Delete
# 'replace' a[i1:i2] should be replaced by b[j1:j2]
[194] Fix | Delete
# 'delete' a[i1:i2] should be deleted
[195] Fix | Delete
# 'insert' b[j1:j2] should be inserted
[196] Fix | Delete
# 'equal' a[i1:i2] == b[j1:j2]
[197] Fix | Delete
# isjunk
[198] Fix | Delete
# a user-supplied function taking a sequence element and
[199] Fix | Delete
# returning true iff the element is "junk" -- this has
[200] Fix | Delete
# subtle but helpful effects on the algorithm, which I'll
[201] Fix | Delete
# get around to writing up someday <0.9 wink>.
[202] Fix | Delete
# DON'T USE! Only __chain_b uses this. Use "in self.bjunk".
[203] Fix | Delete
# bjunk
[204] Fix | Delete
# the items in b for which isjunk is True.
[205] Fix | Delete
# bpopular
[206] Fix | Delete
# nonjunk items in b treated as junk by the heuristic (if used).
[207] Fix | Delete
[208] Fix | Delete
self.isjunk = isjunk
[209] Fix | Delete
self.a = self.b = None
[210] Fix | Delete
self.autojunk = autojunk
[211] Fix | Delete
self.set_seqs(a, b)
[212] Fix | Delete
[213] Fix | Delete
def set_seqs(self, a, b):
[214] Fix | Delete
"""Set the two sequences to be compared.
[215] Fix | Delete
[216] Fix | Delete
>>> s = SequenceMatcher()
[217] Fix | Delete
>>> s.set_seqs("abcd", "bcde")
[218] Fix | Delete
>>> s.ratio()
[219] Fix | Delete
0.75
[220] Fix | Delete
"""
[221] Fix | Delete
[222] Fix | Delete
self.set_seq1(a)
[223] Fix | Delete
self.set_seq2(b)
[224] Fix | Delete
[225] Fix | Delete
def set_seq1(self, a):
[226] Fix | Delete
"""Set the first sequence to be compared.
[227] Fix | Delete
[228] Fix | Delete
The second sequence to be compared is not changed.
[229] Fix | Delete
[230] Fix | Delete
>>> s = SequenceMatcher(None, "abcd", "bcde")
[231] Fix | Delete
>>> s.ratio()
[232] Fix | Delete
0.75
[233] Fix | Delete
>>> s.set_seq1("bcde")
[234] Fix | Delete
>>> s.ratio()
[235] Fix | Delete
1.0
[236] Fix | Delete
>>>
[237] Fix | Delete
[238] Fix | Delete
SequenceMatcher computes and caches detailed information about the
[239] Fix | Delete
second sequence, so if you want to compare one sequence S against
[240] Fix | Delete
many sequences, use .set_seq2(S) once and call .set_seq1(x)
[241] Fix | Delete
repeatedly for each of the other sequences.
[242] Fix | Delete
[243] Fix | Delete
See also set_seqs() and set_seq2().
[244] Fix | Delete
"""
[245] Fix | Delete
[246] Fix | Delete
if a is self.a:
[247] Fix | Delete
return
[248] Fix | Delete
self.a = a
[249] Fix | Delete
self.matching_blocks = self.opcodes = None
[250] Fix | Delete
[251] Fix | Delete
def set_seq2(self, b):
[252] Fix | Delete
"""Set the second sequence to be compared.
[253] Fix | Delete
[254] Fix | Delete
The first sequence to be compared is not changed.
[255] Fix | Delete
[256] Fix | Delete
>>> s = SequenceMatcher(None, "abcd", "bcde")
[257] Fix | Delete
>>> s.ratio()
[258] Fix | Delete
0.75
[259] Fix | Delete
>>> s.set_seq2("abcd")
[260] Fix | Delete
>>> s.ratio()
[261] Fix | Delete
1.0
[262] Fix | Delete
>>>
[263] Fix | Delete
[264] Fix | Delete
SequenceMatcher computes and caches detailed information about the
[265] Fix | Delete
second sequence, so if you want to compare one sequence S against
[266] Fix | Delete
many sequences, use .set_seq2(S) once and call .set_seq1(x)
[267] Fix | Delete
repeatedly for each of the other sequences.
[268] Fix | Delete
[269] Fix | Delete
See also set_seqs() and set_seq1().
[270] Fix | Delete
"""
[271] Fix | Delete
[272] Fix | Delete
if b is self.b:
[273] Fix | Delete
return
[274] Fix | Delete
self.b = b
[275] Fix | Delete
self.matching_blocks = self.opcodes = None
[276] Fix | Delete
self.fullbcount = None
[277] Fix | Delete
self.__chain_b()
[278] Fix | Delete
[279] Fix | Delete
# For each element x in b, set b2j[x] to a list of the indices in
[280] Fix | Delete
# b where x appears; the indices are in increasing order; note that
[281] Fix | Delete
# the number of times x appears in b is len(b2j[x]) ...
[282] Fix | Delete
# when self.isjunk is defined, junk elements don't show up in this
[283] Fix | Delete
# map at all, which stops the central find_longest_match method
[284] Fix | Delete
# from starting any matching block at a junk element ...
[285] Fix | Delete
# b2j also does not contain entries for "popular" elements, meaning
[286] Fix | Delete
# elements that account for more than 1 + 1% of the total elements, and
[287] Fix | Delete
# when the sequence is reasonably large (>= 200 elements); this can
[288] Fix | Delete
# be viewed as an adaptive notion of semi-junk, and yields an enormous
[289] Fix | Delete
# speedup when, e.g., comparing program files with hundreds of
[290] Fix | Delete
# instances of "return NULL;" ...
[291] Fix | Delete
# note that this is only called when b changes; so for cross-product
[292] Fix | Delete
# kinds of matches, it's best to call set_seq2 once, then set_seq1
[293] Fix | Delete
# repeatedly
[294] Fix | Delete
[295] Fix | Delete
def __chain_b(self):
[296] Fix | Delete
# Because isjunk is a user-defined (not C) function, and we test
[297] Fix | Delete
# for junk a LOT, it's important to minimize the number of calls.
[298] Fix | Delete
# Before the tricks described here, __chain_b was by far the most
[299] Fix | Delete
# time-consuming routine in the whole module! If anyone sees
[300] Fix | Delete
# Jim Roskind, thank him again for profile.py -- I never would
[301] Fix | Delete
# have guessed that.
[302] Fix | Delete
# The first trick is to build b2j ignoring the possibility
[303] Fix | Delete
# of junk. I.e., we don't call isjunk at all yet. Throwing
[304] Fix | Delete
# out the junk later is much cheaper than building b2j "right"
[305] Fix | Delete
# from the start.
[306] Fix | Delete
b = self.b
[307] Fix | Delete
self.b2j = b2j = {}
[308] Fix | Delete
[309] Fix | Delete
for i, elt in enumerate(b):
[310] Fix | Delete
indices = b2j.setdefault(elt, [])
[311] Fix | Delete
indices.append(i)
[312] Fix | Delete
[313] Fix | Delete
# Purge junk elements
[314] Fix | Delete
self.bjunk = junk = set()
[315] Fix | Delete
isjunk = self.isjunk
[316] Fix | Delete
if isjunk:
[317] Fix | Delete
for elt in b2j.keys():
[318] Fix | Delete
if isjunk(elt):
[319] Fix | Delete
junk.add(elt)
[320] Fix | Delete
for elt in junk: # separate loop avoids separate list of keys
[321] Fix | Delete
del b2j[elt]
[322] Fix | Delete
[323] Fix | Delete
# Purge popular elements that are not junk
[324] Fix | Delete
self.bpopular = popular = set()
[325] Fix | Delete
n = len(b)
[326] Fix | Delete
if self.autojunk and n >= 200:
[327] Fix | Delete
ntest = n // 100 + 1
[328] Fix | Delete
for elt, idxs in b2j.items():
[329] Fix | Delete
if len(idxs) > ntest:
[330] Fix | Delete
popular.add(elt)
[331] Fix | Delete
for elt in popular: # ditto; as fast for 1% deletion
[332] Fix | Delete
del b2j[elt]
[333] Fix | Delete
[334] Fix | Delete
def find_longest_match(self, alo, ahi, blo, bhi):
[335] Fix | Delete
"""Find longest matching block in a[alo:ahi] and b[blo:bhi].
[336] Fix | Delete
[337] Fix | Delete
If isjunk is not defined:
[338] Fix | Delete
[339] Fix | Delete
Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where
[340] Fix | Delete
alo <= i <= i+k <= ahi
[341] Fix | Delete
blo <= j <= j+k <= bhi
[342] Fix | Delete
and for all (i',j',k') meeting those conditions,
[343] Fix | Delete
k >= k'
[344] Fix | Delete
i <= i'
[345] Fix | Delete
and if i == i', j <= j'
[346] Fix | Delete
[347] Fix | Delete
In other words, of all maximal matching blocks, return one that
[348] Fix | Delete
starts earliest in a, and of all those maximal matching blocks that
[349] Fix | Delete
start earliest in a, return the one that starts earliest in b.
[350] Fix | Delete
[351] Fix | Delete
>>> s = SequenceMatcher(None, " abcd", "abcd abcd")
[352] Fix | Delete
>>> s.find_longest_match(0, 5, 0, 9)
[353] Fix | Delete
Match(a=0, b=4, size=5)
[354] Fix | Delete
[355] Fix | Delete
If isjunk is defined, first the longest matching block is
[356] Fix | Delete
determined as above, but with the additional restriction that no
[357] Fix | Delete
junk element appears in the block. Then that block is extended as
[358] Fix | Delete
far as possible by matching (only) junk elements on both sides. So
[359] Fix | Delete
the resulting block never matches on junk except as identical junk
[360] Fix | Delete
happens to be adjacent to an "interesting" match.
[361] Fix | Delete
[362] Fix | Delete
Here's the same example as before, but considering blanks to be
[363] Fix | Delete
junk. That prevents " abcd" from matching the " abcd" at the tail
[364] Fix | Delete
end of the second sequence directly. Instead only the "abcd" can
[365] Fix | Delete
match, and matches the leftmost "abcd" in the second sequence:
[366] Fix | Delete
[367] Fix | Delete
>>> s = SequenceMatcher(lambda x: x==" ", " abcd", "abcd abcd")
[368] Fix | Delete
>>> s.find_longest_match(0, 5, 0, 9)
[369] Fix | Delete
Match(a=1, b=0, size=4)
[370] Fix | Delete
[371] Fix | Delete
If no blocks match, return (alo, blo, 0).
[372] Fix | Delete
[373] Fix | Delete
>>> s = SequenceMatcher(None, "ab", "c")
[374] Fix | Delete
>>> s.find_longest_match(0, 2, 0, 1)
[375] Fix | Delete
Match(a=0, b=0, size=0)
[376] Fix | Delete
"""
[377] Fix | Delete
[378] Fix | Delete
# CAUTION: stripping common prefix or suffix would be incorrect.
[379] Fix | Delete
# E.g.,
[380] Fix | Delete
# ab
[381] Fix | Delete
# acab
[382] Fix | Delete
# Longest matching block is "ab", but if common prefix is
[383] Fix | Delete
# stripped, it's "a" (tied with "b"). UNIX(tm) diff does so
[384] Fix | Delete
# strip, so ends up claiming that ab is changed to acab by
[385] Fix | Delete
# inserting "ca" in the middle. That's minimal but unintuitive:
[386] Fix | Delete
# "it's obvious" that someone inserted "ac" at the front.
[387] Fix | Delete
# Windiff ends up at the same place as diff, but by pairing up
[388] Fix | Delete
# the unique 'b's and then matching the first two 'a's.
[389] Fix | Delete
[390] Fix | Delete
a, b, b2j, isbjunk = self.a, self.b, self.b2j, self.bjunk.__contains__
[391] Fix | Delete
besti, bestj, bestsize = alo, blo, 0
[392] Fix | Delete
# find longest junk-free match
[393] Fix | Delete
# during an iteration of the loop, j2len[j] = length of longest
[394] Fix | Delete
# junk-free match ending with a[i-1] and b[j]
[395] Fix | Delete
j2len = {}
[396] Fix | Delete
nothing = []
[397] Fix | Delete
for i in range(alo, ahi):
[398] Fix | Delete
# look at all instances of a[i] in b; note that because
[399] Fix | Delete
# b2j has no junk keys, the loop is skipped if a[i] is junk
[400] Fix | Delete
j2lenget = j2len.get
[401] Fix | Delete
newj2len = {}
[402] Fix | Delete
for j in b2j.get(a[i], nothing):
[403] Fix | Delete
# a[i] matches b[j]
[404] Fix | Delete
if j < blo:
[405] Fix | Delete
continue
[406] Fix | Delete
if j >= bhi:
[407] Fix | Delete
break
[408] Fix | Delete
k = newj2len[j] = j2lenget(j-1, 0) + 1
[409] Fix | Delete
if k > bestsize:
[410] Fix | Delete
besti, bestj, bestsize = i-k+1, j-k+1, k
[411] Fix | Delete
j2len = newj2len
[412] Fix | Delete
[413] Fix | Delete
# Extend the best by non-junk elements on each end. In particular,
[414] Fix | Delete
# "popular" non-junk elements aren't in b2j, which greatly speeds
[415] Fix | Delete
# the inner loop above, but also means "the best" match so far
[416] Fix | Delete
# doesn't contain any junk *or* popular non-junk elements.
[417] Fix | Delete
while besti > alo and bestj > blo and \
[418] Fix | Delete
not isbjunk(b[bestj-1]) and \
[419] Fix | Delete
a[besti-1] == b[bestj-1]:
[420] Fix | Delete
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
[421] Fix | Delete
while besti+bestsize < ahi and bestj+bestsize < bhi and \
[422] Fix | Delete
not isbjunk(b[bestj+bestsize]) and \
[423] Fix | Delete
a[besti+bestsize] == b[bestj+bestsize]:
[424] Fix | Delete
bestsize += 1
[425] Fix | Delete
[426] Fix | Delete
# Now that we have a wholly interesting match (albeit possibly
[427] Fix | Delete
# empty!), we may as well suck up the matching junk on each
[428] Fix | Delete
# side of it too. Can't think of a good reason not to, and it
[429] Fix | Delete
# saves post-processing the (possibly considerable) expense of
[430] Fix | Delete
# figuring out what to do with it. In the case of an empty
[431] Fix | Delete
# interesting match, this is clearly the right thing to do,
[432] Fix | Delete
# because no other kind of match is possible in the regions.
[433] Fix | Delete
while besti > alo and bestj > blo and \
[434] Fix | Delete
isbjunk(b[bestj-1]) and \
[435] Fix | Delete
a[besti-1] == b[bestj-1]:
[436] Fix | Delete
besti, bestj, bestsize = besti-1, bestj-1, bestsize+1
[437] Fix | Delete
while besti+bestsize < ahi and bestj+bestsize < bhi and \
[438] Fix | Delete
isbjunk(b[bestj+bestsize]) and \
[439] Fix | Delete
a[besti+bestsize] == b[bestj+bestsize]:
[440] Fix | Delete
bestsize = bestsize + 1
[441] Fix | Delete
[442] Fix | Delete
return Match(besti, bestj, bestsize)
[443] Fix | Delete
[444] Fix | Delete
def get_matching_blocks(self):
[445] Fix | Delete
"""Return list of triples describing matching subsequences.
[446] Fix | Delete
[447] Fix | Delete
Each triple is of the form (i, j, n), and means that
[448] Fix | Delete
a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in
[449] Fix | Delete
i and in j. New in Python 2.5, it's also guaranteed that if
[450] Fix | Delete
(i, j, n) and (i', j', n') are adjacent triples in the list, and
[451] Fix | Delete
the second is not the last triple in the list, then i+n != i' or
[452] Fix | Delete
j+n != j'. IOW, adjacent triples never describe adjacent equal
[453] Fix | Delete
blocks.
[454] Fix | Delete
[455] Fix | Delete
The last triple is a dummy, (len(a), len(b), 0), and is the only
[456] Fix | Delete
triple with n==0.
[457] Fix | Delete
[458] Fix | Delete
>>> s = SequenceMatcher(None, "abxcd", "abcd")
[459] Fix | Delete
>>> list(s.get_matching_blocks())
[460] Fix | Delete
[Match(a=0, b=0, size=2), Match(a=3, b=2, size=2), Match(a=5, b=4, size=0)]
[461] Fix | Delete
"""
[462] Fix | Delete
[463] Fix | Delete
if self.matching_blocks is not None:
[464] Fix | Delete
return self.matching_blocks
[465] Fix | Delete
la, lb = len(self.a), len(self.b)
[466] Fix | Delete
[467] Fix | Delete
# This is most naturally expressed as a recursive algorithm, but
[468] Fix | Delete
# at least one user bumped into extreme use cases that exceeded
[469] Fix | Delete
# the recursion limit on their box. So, now we maintain a list
[470] Fix | Delete
# ('queue`) of blocks we still need to look at, and append partial
[471] Fix | Delete
# results to `matching_blocks` in a loop; the matches are sorted
[472] Fix | Delete
# at the end.
[473] Fix | Delete
queue = [(0, la, 0, lb)]
[474] Fix | Delete
matching_blocks = []
[475] Fix | Delete
while queue:
[476] Fix | Delete
alo, ahi, blo, bhi = queue.pop()
[477] Fix | Delete
i, j, k = x = self.find_longest_match(alo, ahi, blo, bhi)
[478] Fix | Delete
# a[alo:i] vs b[blo:j] unknown
[479] Fix | Delete
# a[i:i+k] same as b[j:j+k]
[480] Fix | Delete
# a[i+k:ahi] vs b[j+k:bhi] unknown
[481] Fix | Delete
if k: # if k is 0, there was no matching block
[482] Fix | Delete
matching_blocks.append(x)
[483] Fix | Delete
if alo < i and blo < j:
[484] Fix | Delete
queue.append((alo, i, blo, j))
[485] Fix | Delete
if i+k < ahi and j+k < bhi:
[486] Fix | Delete
queue.append((i+k, ahi, j+k, bhi))
[487] Fix | Delete
matching_blocks.sort()
[488] Fix | Delete
[489] Fix | Delete
# It's possible that we have adjacent equal blocks in the
[490] Fix | Delete
# matching_blocks list now. Starting with 2.5, this code was added
[491] Fix | Delete
# to collapse them.
[492] Fix | Delete
i1 = j1 = k1 = 0
[493] Fix | Delete
non_adjacent = []
[494] Fix | Delete
for i2, j2, k2 in matching_blocks:
[495] Fix | Delete
# Is this block adjacent to i1, j1, k1?
[496] Fix | Delete
if i1 + k1 == i2 and j1 + k1 == j2:
[497] Fix | Delete
# Yes, so collapse them -- this just increases the length of
[498] Fix | Delete
# the first block by the length of the second, and the first
[499] Fix | Delete
It is recommended that you Edit text format, this type of Fix handles quite a lot in one request
Function