2016-06-01 5 views
0

Я хотел бы сравнить два отсортированных списков поэлементно и обрабатывать каждый случай по-разному:Python: сравнение двух итерируемыми поэлементно с различным типом

  1. Если оба итерируемыми содержат элемент, я хотел бы назвать update_func.
  2. Если только левый итерируемый содержит элемент, я бы хотел позвонить left_surplus_func.
  3. Если только правильный итеративный элемент содержит элемент, я бы хотел позвонить right_surplus_func.

К сожалению, zip не помогает мне в этом случае, так как он создавал кортежи несвязанных элементов. Кроме того, в моем случае оба списка или итерации имеют разные и неконвертируемые типы.

Это похоже на How can I compare two lists in python and return matches и Checking if any elements in one list are in another, но недостаточно близко, чтобы быть реальным решением.

я придумал с рабочим раствором (кроме как итерируемые не должен содержать None):

def compare_iterables_elemet_wise(left, right, compare_func, 
          left_surplus_func, update_func, right_surplus_func): 
    """ 
    :type left: collections.Iterable[U] 
    :type right: collections.Iterable[T] 
    :type compare_func: (U, T) -> int 
    :type left_surplus_func: (U) -> None 
    :type update_func: (U, T) -> None 
    :type right_surplus_func: (T) -> None 
    """ 
    while True: 
     try: 
      l = next(left) 
     except StopIteration: 
      l = None # Evil hack, but acceptable for this example 
     try: 
      r = next(right) 
     except StopIteration: 
      r = None 

     if l is None and r is not None: 
      cmp_res = 1 
     elif l is not None and r is None: 
      cmp_res = -1 
     elif l is None and r is None: 
      return 
     else: 
      cmp_res = compare_func(l, r) 

     if cmp_res == 0: 
      update_func(l, r) 
     elif cmp_res < 0: 
      left_surplus_func(l) 
      right = itertools.chain([r], right) # aka right.unget(r) 
     else: 
      right_surplus_func(r) 
      left = itertools.chain([l], left) # aka left.unget(l) 

Есть ли более вещий способ архивировать подобный результат? Я немного недоволен своим решением, так как это зависит от внешних побочных эффектов функций. Было бы неплохо иметь чистое функциональное решение.

Edit: Это мой тест:

creates = [] 
updates = [] 
deletes = [] 

def compare(a, obj): 
    return cmp(int(a), obj) 

def handle_left(a): 
    creates.append(a) 

def update(a, obj): 
    updates.append((a, obj)) 

def handle_right(obj): 
    deletes.append(obj) 

left = list('12356') 
right = [1, 3, 4, 6, 7] 
compare_iterables_elemet_wise(iter(left), iter(right), compare, handle_left, update, handle_right) 

assert creates == ['2', '5'] 
assert updates == [('1', 1), ('3', 3), ('6', 6)] 
assert deletes == [4, 7] 

Я думаю, что мне просто нужно эти три списка: creates, updates и deletes.

Edit2: набор операций:

Это похоже на мою проблему, за исключением того, что типы слева и справа отличаются:

left = [1, 2, 3, 5, 6] 

right = [1, 3, 4, 6, 7] 

In [10]: set(left) - set(right) 
Out[10]: {2, 5} 

In [11]: set(right) - set(left) 
Out[11]: {4, 7} 


In [14]: set(right).intersection(set(left)) 
Out[14]: {1, 3, 6} 
+0

- это два списка с одинаковой длиной? Думаю, не по твоей проблеме? Помогло бы, если бы вы указали простой общий пример вместо своего конкретного кода. –

+0

@ColonelBeauvel: здесь вы идете. –

+1

То, что вы пытаетесь достичь, не является, с моей точки зрения, очевидным ..., поскольку такой набор правил между списками (после преобразования) может помочь в вашей проблеме? –

ответ

1

Вы можете упростить try заявления в начале части вашего while, используя аргумент по умолчанию для встроенной функции next.

def compare_iterables_element_wise(left, right, compare_func, 
         left_surplus_func, update_func, right_surplus_func): 

    l, r = next(left, None), next(right, None) 
    while l or r: 
     if l is None and r is not None: 
      cmp_res = 1 
     elif l is not None and r is None: 
      cmp_res = -1 
     else: 
      cmp_res = compare_func(l, r) 

     if cmp_res == 0: 
      update_func(l, r) 
      l, r = next(left, None), next(right, None) 
     elif cmp_res < 0: 
      left_surplus_func(l) 
      l = next(left, None) 
     else: 
      right_surplus_func(r) 
      r = next(right, None) 

Я также хотел бы предложить, что compare_func обрабатывает случай, когда один из аргументов является None так, что вы избавляетесь от первого if.

С вашего второго редактирования использование наборов кажется хорошей идеей, предоставляя возможность превращать ваши объекты в общий тип. У вас может быть метод __hash__ для связывания целого с каждым объектом и выполнять операции на основе значения hash.