Уменьшить переменную, которую я увеличиваю в цикле for

новичок в Python, и я немного осмотрелся, но не нашел удовлетворительного ответа на свой вопрос. Я решаю некоторые практические задачи и хочу создать метод, который избавляется от повторяющихся значений в списке. Пока это мой код:

def noDouble(nums):
  for x in xrange(len(nums) - 2):
      if nums[x] == nums[x + 1]:
          nums.pop(x)
          x -= 1
  return nums

Что я хочу, так это то, что если есть дубликат, вытащите один из дубликатов, а затем снова вернитесь (так что, если есть, скажем, 3 экземпляра одного и того же числа, он избавится от всех из них, ' перемотка»).

Я ищу объяснение, почему мой код не работает, а также объяснение решения, и я был бы очень признателен за любую помощь. Спасибо.


person iFluctuate    schedule 20.07.2014    source источник


Ответы (3)


Потому что for не считает числа; он перебирает последовательность. Каждый раз, когда выполняется тело цикла, x устанавливается на следующее значение, созданное xrange. Оператору for все равно, что x будет в конце цикла.

В любом случае вам действительно не следует изменять список внутри цикла. Более простой способ сделать это — создать новый список:

def remove_doubles(old):
    new = []
    for item in old:
        if new and item == new[-1]:
            continue
        new.append(item)
    return new

Конечно, действительно самый простой способ удалить дубликаты — это list(set(foo)). Но это удалит все дубликаты, а не только соседние, и, вероятно, также уничтожит порядок элементов.

person Eevee    schedule 20.07.2014
comment
Обратите внимание, что set будет вести себя иначе, чем код OP. Его подход (если бы он работал) сравнивает каждый элемент только со следующим элементом, поэтому он будет сворачивать только непрерывные последовательности дубликатов, но set удалит все дубликаты. Однако неясно, какое поведение здесь на самом деле желательно. - person BrenBarn; 20.07.2014
comment
continue переходит к следующему запуску цикла; item == new[-1] проверяет, равно ли item последнему элементу в списке new. - person Eevee; 20.07.2014
comment
@ Иви, зачем нужен new? Почему первая часть этого утверждения and? - person iFluctuate; 20.07.2014
comment
потому что если new пусто, нет последнего элемента для сравнения. - person Eevee; 20.07.2014
comment
@ Иви, спасибо. Я не знал, что вы можете просто использовать имя списка и получить логическое значение (я предполагаю), в зависимости от того, содержит ли он что-либо. Я выбрал ваш ответ из-за вашего удивительного ответа. Мне также интересно, есть ли другой способ сделать это, более похожий на мой, вроде циклов Java for, которые не сбрасывают счетчики, чтобы игнорировать изменения, внесенные в счетчик внутри цикла. - person iFluctuate; 20.07.2014
comment
вы можете сделать логический тест для чего угодно; как правило, любой пустой контейнер или нулевое значение будут проверяться как ложные. и нет, нельзя. for в стиле C — это просто более короткий способ написания цикла while, и такого сокращения не существует в Python, потому что это очень редко то, что вам действительно нужно. вы можете написать это как while, если вы действительно хотите возиться со своим счетчиком циклов, но обычно есть более простые способы сделать то, что вы хотите. (например, ваш подход можно заставить работать, выполняя итерацию назад с конца списка.) - person Eevee; 20.07.2014
comment
Возможно, стоит отметить существование itertools.groupby. [val for val, group in groupby(nums)] создает новый список с любыми группами дубликатов, свернутыми в одно вхождение элемента. - person user2357112 supports Monica; 20.07.2014

Насколько я понимаю, цикл for не является простым приращением, как в C или Java; Python фактически заставит x вернуться к ожидаемому значению для следующей итерации цикла. Таким образом, уменьшение x не приведет к добавлению дополнительных итераций цикла, потому что оно будет принудительно сброшено.

for x in range(10):
x -= 1
print x

даст

-1
0
1
2
3
4
5
6
7
8

РЕДАКТИРОВАТЬ: вот возможность того, что вы пытаетесь сделать, хотя принятый ответ проще для вашего конкретного случая использования. Используйте while вместо for:

limit = len(nums) - 1
x = 0
while x < limit:
  if nums[x] == nums[x+1]:
    nums.pop(x)
    x -= 1
  x += 1
return nums

Хотя этот код все еще может завершиться ошибкой с IndexError из-за того, как вы обращаетесь к элементам списка. Но тем не менее, именно так вы добавляете дополнительные итерации.

person TheSoundDefense    schedule 20.07.2014
comment
На самом деле Java был моим первым языком, и я был уверен, что он будет работать, основываясь исключительно на моем опыте работы с Java. Просто идет, чтобы показать, как много есть, чтобы узнать там. Спасибо за объяснение, мужчина/женщина. Это действительно прояснило ситуацию и дало мне некоторую перспективу. - person iFluctuate; 20.07.2014
comment
Большое спасибо за редактирование. Однако не следует ли увеличивать x в этом цикле while? - person iFluctuate; 20.07.2014
comment
@user3236859 user3236859 упс, так и должно быть. - person TheSoundDefense; 20.07.2014
comment
еще раз спасибо. К сожалению, я уже проверил ответ, но хотел бы проверить несколько (хотя я действительно не знаю, что делают эти проверки). - person iFluctuate; 20.07.2014
comment
@user3236859 user3236859 стрелка вверх указывает на полезный ответ, а галочка указывает на то, что данный ответ был правильным ответом, который решил вашу конкретную проблему, или любым другим подходящим аналогом. - person TheSoundDefense; 20.07.2014
comment
~~ это только что получило мета~~ Недостаточно репутации, чтобы проголосовать за вас, но как только я соберу этот сладкий сладкий нектар богов, который является репутацией, я вернусь. - person iFluctuate; 20.07.2014

Как правило, вы никогда не захотите манипулировать счетчиком в цикле (кроме установки выхода из цикла). По той же причине вы не хотите пилить ветку, на которой сидите, когда рубите дерево :-)

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

person Kevin Seifert    schedule 20.07.2014