views:

282

answers:

8

Basically, I need a way to return control to the beginning of a for loop and actually restart the entire iteration process after taking an action if a certain condition is met.

What I'm trying to do is this:

    for index, item in enumerate(list2):
    if item == '||' and list2[index-1] == '||':
        del list2[index]
        *<some action that resarts the whole process>*

That way, if ['berry','||','||','||','pancake] is inside the list, I'll wind up with:

['berry','||','pancake'] instead.

Thanks!

+2  A: 

Continue will work for any loop.

volting
But I'm looking for a way to restart the for loop entirely...control goes back to the state it was in at the beginning.
Georgina
Good reference for this: http://en.wikibooks.org/wiki/Python_Programming/Flow_control#Breaking.2C_continuing_and_the_else_clause_of_loops
Aaron D
+2  A: 

continue works in for loops also.

>>> for i in range(3):
...     print 'Before', i
...     if i == 1:
...             continue
...     print 'After', i
... 
Before 0
After 0
Before 1
# After 1 is missing
Before 2
After 2
sdolan
Thanks--that's good to know! But I'm looking for a way to restart the for loop entirely...control goes back to the state it was in at the beginning.
Georgina
+10  A: 

I'm not sure what you mean by "restarting". Do you want to start iterating over from the beginning, or simply skip the current iteration?

If it's the latter, then for loops support continue just like while loops do:

for i in xrange(10):
  if i == 5:
    continue
  print i

The above will print the numbers from 0 to 9, except for 5.

If you're talking about starting over from the beginning of the for loop, there's no way to do that except "manually", for example by wrapping it in a while loop:

should_restart = True
while should_restart:
  should_restart = False
  for i in xrange(10):
    print i
    if i == 5:
      should_restart = True
      break

The above will print the numbers from 0 to 5, then start over from 0 again, and so on indefinitely (not really a great example, I know).

Liquid_Fire
Thanks a hell of a lot! The second solution is brilliant.
Georgina
No problem. Also have a look at Nathon's answer, it has a cleaner/simpler version of this using `for`'s `else` clause.
Liquid_Fire
+12  A: 
while True:
    for i in xrange(10):
        if condition(i):
            break
    else:
        break

That will do what you seem to want. Why you would want to do it is a different matter. Maybe you should take a look at your code and make sure you're not missing an obvious and easier way to do it.

Nathon
+4  A: 

some action that resarts the whole process

A poor way to think of an algorithm.

You're just filtering, i.e., removing duplicates.

And -- in Python -- you're happiest making copies, not trying to do del. In general, there's very little call to use del.

def unique( some_list ):
    list_iter= iter(some_list)
    prev= list_iter.next()
    for item in list_iter:
        if item != prev:
            yield prev
            prev= item
    yield prev

list( unique( ['berry','||','||','||','pancake'] ) )
S.Lott
He's removing adjacent duplicates, to be clear. It seems this is what your method does, but it's not what is generally meant by the name 'unique'.
David M.
@David M.: While true, that's a kind of "negative" comment. "unique isn't appropriate". Okay. What do you suggest? Can you make a "positive" comment that proposes something?
S.Lott
Sure, if you need that kind of coddling. (For what it's worth, I upvoted your answer; I'm not trying to tear you down, just don't want the OP to be confused.) The `itertools` recipes call it `unique_justseen`, perhaps `unique_adjacent` would be better.
David M.
@David M: "trying to tear you down" What? You pointed out a possible error. Why not take the next logical step and make a suggestion? It's not "coddling" -- it's an approach to providing value. Saying something is "not what is generally meant" has minimal value. Improving on an answer adds more value. Pointing out an error doesn't add as much value. I'm not asking for coddling. I'm asking you to create as much value as possible. You must have some insight. Share it.
S.Lott
My apologies, I took your comment the wrong way. It's probably from being surrounded by HR people.
David M.
+2  A: 

The inevitable itertools version, because it just came to me:

from itertools import groupby

def uniq(seq):
    for key, items in groupby(seq):
        yield key

print list(uniq(['berry','||','||','||','pancake'])) # ['berry','||', 'pancake']
# or simply:
print [key for key, items in groupby(['berry','||','||','||','pancake'])]
THC4k
+1, I would have just returned a generator expression though
gnibbler
A: 
def remove_adjacent(nums):
     return [a for a,b in zip(nums, nums[1:]+[not nums[-1]]) if a != b]

example = ['berry','||','||','||','pancake']

example = remove_adjacent(example)
print example
""" Output:
['berry', '||', 'pancake']
"""

And by the way this is repeating of http://stackoverflow.com/questions/3460161/remove-adjacent-duplicate-elements-from-a-list/3463143#3463143

Tony Veijalainen
A: 

As you can see answering your question leads to some rather convoluted code. Usually a better way can be found, which is why such constructs aren't built into the language

If you are not comfortable using itertools, consider using this loop instead. Not only is it easier to follow than your restarting for loop, it is also more efficient because it doesn't waste time rechecking items that have already been passed over.

L = ['berry','||','||','||','pancake']
idx=1
while idx<len(L):
    if L[idx-1]==L[idx]:
        del L[idx]
    else:
        idx+=1
gnibbler