Частичное совпадение кортежей

У меня есть кортеж кортежей и кортеж. Мне интересно знать, какие элементы первого кортежа соответствуют второму кортежу (если есть), учитывая также частичные совпадения.

Это функция фильтра, чтобы продемонстрировать, что я имею в виду.

def f(repo):
    pattern = (None, None, '1.3')
    for idx, item in enumerate(pattern):
        if item != None and item != repo[idx]:
            return False
    return True

>>> repo = (('framework', 'django', '1.3'), ('cms', 'fein', '1.3'), ('cms', 'django-cms', '2.2'))
>>> filter(f, repo)
(('framework', 'django', '1.3'), ('cms', 'fein', '1.3'))

Фильтр бесполезен в этой форме, потому что шаблон не может быть предоставлен извне в качестве аргумента (я хочу использовать одну и ту же функцию для проверки разных входных данных). Есть ли способ исправить это?

И какой еще алгоритм можно использовать для лучшего подхода к исходной проблеме?


person Paolo    schedule 24.08.2011    source источник
comment
Всегда используйте оператор is при проверке None.   -  person Tyler Crompton    schedule 21.11.2012


Ответы (5)


Вы можете использовать замыкание для привязки шаблона к функции:

def matcher(pattern):
    def f(repo):
        return all(p is None or r == p for r, p in zip(repo, pattern))
    return f

>>> repo = (('framework', 'django', '1.3'), ('cms', 'fein', '1.3'), ('cms', 'django-cms', '2.2'))
>>> pattern = (None, None, '1.3')
>>> filter(matcher(pattern), repo)
(('framework', 'django', '1.3'), ('cms', 'fein', '1.3'))

Я также предоставил другое выражение для сравнения кортежей.

person Ned Batchelder    schedule 24.08.2011

Почему бы вам не использовать встроенный filter:

>>> filter(lambda x: x[2] == '1.3', repo)
<<< (('framework', 'django', '1.3'), ('cms', 'fein', '1.3'))

...или понимание списка:

>>> [x for x in repo if x[2] == '1.3']
<<< [('framework', 'django', '1.3'), ('cms', 'fein', '1.3')]

Если вы хотите обернуть его в функцию:

types = {'desc': 0, 'name': 1, 'version': 2}
def repo_filter(type, critera, repo=repo, types=types):
    return [x for x in repo if x[types[type]] == critera]

>>> repo_filter('version', '1.3')
<<< [('framework', 'django', '1.3'), ('cms', 'fein', '1.3')]
person zeekay    schedule 24.08.2011

Что о:

def f(repo, pattern=None):
    if not pattern:
        pattern = (None, None, '1.3')
    for idx, item in enumerate(pattern):
        if item and item != repo[idx]:
            return False
    return True

repo = (('framework', 'django', '1.3'), ('cms', 'fein', '1.3'), ('cms', 'django-cms', '2.2'))

[x for x in repo if f(x)]
>>>[('framework', 'django', '1.3'), ('cms', 'fein', '1.3')]    

[x for x in repo if f(x, ('cms',None, None))]
>>> [('cms', 'fein', '1.3'), ('cms', 'django-cms', '2.2')]
person Artsiom Rudzenka    schedule 24.08.2011

Вы можете использовать следующее выражение:

repo = (('framework', 'django', '1.3'), ('cms', 'fein', '1.3'), ('cms', 'django-cms', '2.2'))
p = (None, None, '1.3')
matches = [i for i in repo if i[0]==p[0] or i[1]==p[1] or i[2]==p[2]]

или используйте закрытие, например:

def matcher(pattern):
    def pattern_matcher(repo):
        for idx, item in enumerate(pattern):
            if item is not None and item != repo[idx]:
                return False
        return True
    return pattern_matcher

а затем может быть вызван следующим образом:

filter(matcher(pattern), repo)
person Tadeck    schedule 24.08.2011
comment
Всегда используйте оператор is при проверке None. - person Tyler Crompton; 21.11.2012
comment
@TylerCrompton: Спасибо, вы, очевидно, правы. Но это не единственная проблема кода. Я изменил != None на is not None и исправил проблему с возвратом True слишком рано. Теперь должно быть лучше. - person Tadeck; 21.11.2012
comment
Я только что просмотрел код и заметил одну незначительную деталь. Я прокомментировал все, чтобы любой будущий читатель знал, как это сделать правильно. :) - person Tyler Crompton; 23.11.2012

person    schedule
comment
Всегда используйте оператор is при проверке None. - person Tyler Crompton; 21.11.2012