All pastes #420295 Raw Edit

Something

public text v1 · immutable
·published 2007-04-02 01:16 UTC
rendered paste body
def getind(seq, i, default):
    """Convience function"""
    try: return seq[i]
    except LookupError: return default


fuzzycache = dict()  #cached results of fuzzydistance()

def clearcache():
    """Clear the fuzzydistance() cache"""
    fuzzycache.clear()

def fuzzydistance(a, b):
    """
    Calculates the number of edit operations it takes to transform string a and string b.

    The possible operations are each weighted with different values

        delete     - 3 points
        insert     - 3 points
        swap       - 2 points
        substitute - 2 points
        strip      - 1 point

    The weights are intended to detect abbreviations and typos as closely as possible.

    The lower the return value, the more similst the strings are.
    """
    try:
        return fuzzycache[a,b]
    except KeyError:
        dist = _fuzzydist(a, b)
        fuzzycache[a,b] = dist
        return dist

def _fuzzydist(x, y, dist=0):
    xchar = getind(x, 0, None)
    ychar = getind(y, 0, None)
    nextxchar = getind(x, 1, None)
    nextychar = getind(y, 1, None)
    
    if xchar == None:
        return dist + len(y)
    elif ychar == None:
        return dist + (len(x) * 3)
    else:
        options = []
        if xchar == ychar:
            options.append((0, 1, 1))
        else:
            options.append((2, 1, 1))
            
        if (xchar, nextxchar) == (nextychar, ychar):
            options.append((2, 2, 2))
        elif xchar == nextychar:
            options.append((3, 0, 1))
        elif ychar == nextxchar:
            options.append((3, 1, 0))

        return min(_fuzzydist(x[xi:], y[yi:], dist+d) for d,xi,yi in options)


def test(seq):
    import profile
    for a,b in seq:
        profile.runctx("fuzzydistance(a,b)", globals(), locals())
        print "fuzzydistance(%r, %r) == %r" % (a,b, fuzzydistance(a,b))
        print "------------------------------"
        #fuzzydistance(a,b)
        



if __name__ == "__main__":
    test(
        [("Goffredo", "Geoffrey"),
         ("Goffredo", "Gofredo"),
         ("Lea", "Leanna"),
         ("Brit", "Britanny"),
         ("Britanny", "Brit"),
         ("Kurt", "Krut"),
         ("Hippo", "Haiku"),
         ("bicker", "bcisjfeflge")]
        )