Fine-grained, fuzzy matching in diff

When comparing structures which have no primary keys, the code used to
require an exact match on all fields to match them up.  In practice, this
resulted in only 'removed'/'added' diffs ever being generated for one
typical case: comparing data structures which have had surrogate IDs
stripped.  Add a new function 'fuzzy match' which tries to match up 'added'
and 'removed' items from collections, so that the diff mechanism can
descend further and return more fine-grained differences.