-1

I'm doing some practice work and I am tasked with removing the first number from a string until it is odd.

The function should remove elements from the front of lst until the front of the list is not even. The function should then return lst.

For example if lst started as [4, 8, 10, 11, 12, 15], then delete_starting_evens(lst) should return [11, 12, 15].

def delete_starting_evens(lst):
  for i in lst:
    if i % 2 == 0:
      lst.pop(0)
    else:
      break
  return lst

print(delete_starting_evens([4, 8, 6, 6, 6, 10, 11, 12, 15]))

print(delete_starting_evens([4, 8, 10]))

def delete_starting_evens(lst):
  for i in lst:
    if i % 2 == 0:
      lst = lst[1:]
    else:
      break
  return lst

print(delete_starting_evens([4, 8, 6, 6, 6, 10, 11, 12, 15]))

print(delete_starting_evens([4, 8, 10]))`

The code works as intended if i use lst = lst[1:], but I do not understand why the lst.pop(0) version does not work. It will work for a few iterations, but then no longer pops.

Community
  • 1
  • 1
Sweezog
  • 1
  • 2

1 Answers1

1

Modifying a list while iterating over it is usually a recipe to get weird results. Try printing lst and i at every iteration and you'll see what's going on... (Try with lst = [2,4,6,8,10,...] to make it clearer...)

Here's one safe way to do it:

def delete_starting_evens(lst):
    for i, x in enumerate(lst):
        if x % 2 != 0:
            break
    else:
        return []
    return lst[i:]

Note that pop mutates your list, while lst = lst[1:] reassigns it.

If efficiency matters:

def f():
    lst = list(range(0,10000,2))+[3,0,2,4,6]
    for i, x in enumerate(lst):
        if x % 2:
            break
    else:
        return []
    return lst[i:]

def g():
    lst = list(range(0,10000,2))+[3,0,2,4,6]
    while lst and lst[0] % 2 == 0:
        lst.pop(0)
    return lst

%timeit f()
# 334 µs ± 2.17 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

%timeit g()
# 2.09 ms ± 12.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Julien
  • 13,986
  • 5
  • 29
  • 53
  • 2
    A much simpler solution is `while lst[0] % 2 == 0: lst.pop(0)` – tripleee Aug 26 '19 at 04:24
  • (you actually need to check for `while lst and lst[0] % 2 == 0` to be safe...) – Julien Aug 26 '19 at 04:28
  • Thanks, good point. Or make it an entry condition: `if lst: while lst[0] % 2 == 0:` – tripleee Aug 26 '19 at 04:32
  • 1
    `list.pop(0)` is an O(n) operation, so I don't recommend doing it in a loop. Your posted solution is way better. – ggorlen Aug 26 '19 at 05:06
  • 1
    @ggorlen yes I was going to post about it, but I had a bug in my comparison test which was wrongly showing the opposite :). Now fixed and added. – Julien Aug 26 '19 at 05:48
  • FYI, there's a builtin that comes with itertools that is worth mentioning: `itertools.dropwhile(lambda x: x % 2 == 0, lst)`. You might consider editing your post on the benchmark to explain why `.pop(0)` is so slow (moves all the elements in the list down every time). – ggorlen Aug 26 '19 at 06:16