2013-02-13 1 views
5

У меня есть кортеж, созданный с помощью zip(), и мне нужно вычесть его из каждого целого в кортеже. Я пробовал следующее, но, по-видимому, он работает только в списках, так как бы я адаптировал его для кортежей в Python?Как вычесть одно из каждого значения в кортеже в Python?

[...] 
lower, upper = zip(*table) 
lower[:] = [x + 1 for x in lower] 
upper[:] = [x - 1 for x in upper] 
holes = zip(lower[:-1], upper[1:]) 

TypeError: 'tuple' object does not support item assignment

Большая картина у меня есть ряд непересекающихся отсортированных интервалов, хранящихся в table, и мне нужно, чтобы получить ряд отверстий. Например. мой стол интервалы могут быть:

[ 6, 7] 
[ 8, 9] 
[14, 18] 
[23, 32] 

И я хочу, чтобы вычислить holes между сеансами:

[10, 13] 
[19, 22] 
+0

После многих рассуждений, которые помогли @gnibbler, и многочисленных изменений моего кода, я закончил с окончательным. Смотрите, пожалуйста, и скажите мне, что вы думаете об этом, пожалуйста. – eyquem

+0

@eyquem Чтобы улучшить ваш ответ, я бы отредактировал комментарий о том, как вы достигли ответа, чтобы сделать ответ коротким и сладким. – WilliamKF

ответ

2

Использование функции генератора облегчает задачу.

table = [(2,3),(5,6),(12,20),(21,25),(28,28),(35,48),(53,55)] 

def gaps_between(intervals): 
    prec = intervals[0][1] + 1 
    for L,H in intervals: 
     print '\nprec = %d (L,H) = (%d,%d)' % (prec,L,H) 
     print 'prec = %d <= L-1 = %d : %s' % (prec,L-1,prec<=L) 
     if prec<=L-1: 
      yield (prec,L-1) 
     prec = H + 1 
     print 'next prec = %d' % prec 

holes = list(gaps_between(table)) 

print 
print 'table =',table 
print 'holes =',holes 

В прежнем ответе я использовал итератор, определенный внутри генератора.
Чтобы избежать этого, я здесь выше использую стратагема:
, определяющий first prec = first H = intervals[0][1].
Учитывая, что H>=L для каждой пары (L, H), это приводит к
first H > first L - 1 ->first prec > first L - 1.
Следовательно, первое испытание относительно первого интервала всегда False, и реальный процесс начинается со второго интервала.

prec = 3 (L,H) = (2,3) 
prec = 3 <= L-1 = 1 : False 
next prec = 4 

prec = 4 (L,H) = (5,6) 
prec = 4 <= L-1 = 4 : True 
next prec = 7 

prec = 7 (L,H) = (12,20) 
prec = 7 <= L-1 = 11 : True 
next prec = 21 

prec = 21 (L,H) = (21,25) 
prec = 21 <= L-1 = 20 : True 
next prec = 26 

prec = 26 (L,H) = (28,28) 
prec = 26 <= L-1 = 27 : True 
next prec = 29 

prec = 29 (L,H) = (35,48) 
prec = 29 <= L-1 = 34 : True 
next prec = 49 

prec = 49 (L,H) = (53,55) 
prec = 49 <= L-1 = 52 : True 
next prec = 56 

table = [(2, 3), (5, 6), (12, 20), (21, 25), (28, 28), (35, 48), (53, 55)] 
holes = [(4, 4), (7, 11), (26, 27), (29, 34), (49, 52)] 

Результат правильно:
- это дает зазор (4,4) между (2,3) и (5,6)
- нет никакого зазора между (12,20) и (21,25)
- значение 28 присутствует в (28,28), не имеет пробелов

ОП говорит, что интервалы не перекрываются и сортируются.
Однако, испытание if prec<=L-1 является обязательным, в противном случае смежные интервалы дают ошибку:
без этого теста, результат будет содержать
....., (7, 11), (21, 20), (26, 27), .......

.

Случается, что с этим обязательным испытанием следующие списки интервалов
[[ 8, 9],[14, 18],[18, 32]]
[[8, 9], [14, 18], [19, 20], [16, 21], [23, 32]]
, которые чрезмерно притирки (не то, что говорит OP)
и что дало бы ошибку без теста,
дон Фактически, не возникает никаких ошибок.

Правило для списка интервалов для предоставления правильного списка пробелов с моим приведенным выше кодом состоит в том, что интервалы должны сортироваться по второму элементу.

.

Замена yield (prec,L-1) на yield range(prec,L) даст пробелы как диапазоны.

Замена yield (prec,L-1) на holes.append((prec,L-1)), например, позволяет написать код без какой-либо функции.

+0

Хранение 'prec' в генераторе обеспечивает удобное для чтения решение. Вам не нужно предложение 'else', просто переместите' prec = b' после блока 'if'. OP теперь говорит, что пары отсортированы и не перекрываются, поэтому вам не нужно тестировать эти случаи. Но я думаю, что, вероятно, имеет смысл подавить «пустые отверстия», такие как (4, 4) и (22, 22) с выхода, если они могут произойти. –

+0

Ах! вы правы для строки '' prec = b''. Instrcutions '' a + = 1'' и '' b + = 1'' также могут быть удалены, если '' prec = b + 1''. - Но для (4,4) и (22,22) это правильно: они означают пробелы только одного числа. - Я представил себе другие решения, основанные на выводах, непосредственно являющихся диапазонами. - Спасибо за ваш комментарий. – eyquem

+0

Если вы хотите избежать изменения таблицы, один из способов - просто установить «prec = None» изначально и протестировать его в цикле –

6

Используйте tuple конструктор с выражением генератора:

lower = tuple(x - 1 for x in lower) 
upper = tuple(x + 1 for x in upper) 
+0

, чтобы обеспечить результат, необходимый OP, операторы и нарезка должны быть изменены следующим образом: 'holes = zip ([x + 1 для x в верхнем] [: - 1], [x - 1 для x в нижнем ] [1:]) ' – furins

+0

Да, вы можете сделать это в одной строке, но я думаю, что он выглядит более чистым, разделенным на несколько строк. (Это только мое мнение). – nneonneo

+0

извините nneonneo мой комментарий был связан с возвратом ожидаемого ответа, а не поместить его в одну строку (это было просто для простоты, будучи только комментарием). Я отредактировал свой комментарий, чтобы прояснить этот аспект (см. Также комментарий ogzd) – furins

6

Вы можете также просто выработать их в едином перечне:

holes = [(table[i][1]+1, table[i+1][0]-1) for i in range(len(table)-1)] 
+0

А я написал ту же строку, но забыл о нем, желая удалить первый элемент нижнего и последнего элемента верхнего. думаю, что это решение гораздо более pythonic. – placeybordeaux

+0

@ogzd, да, похоже, что это то, что нужно сейчас, есть пример –

+0

, чтобы быть точнее, ответ должен быть «holes = zip ([x + 1 для x в верхнем]] [ : -1], [x - 1 для x в нижнем] [1:]) '(список списков), чтобы соответствовать данному примеру ...;) – furins