2

So I understand placing even numbers in a new list but when there are a number of odd numbers in front I can only seem to call -1? and for an empty string I receive 'nothing' when it should also return -1

def first_even(items):
    """ (list of int) -> int

    Return the first even number from items. Return -1 if items     contains no even numbers.

    >>> first_even([1, 9, 3, 77, 13, 5, 8])
    8
    >>> first_even([7, 1])
    -1
    """
    even = []
    for num in items:
        if num % 2 == 0:
            even.append(num)
        else:
            return -1
    return even[0]
martineau
  • 119,623
  • 25
  • 170
  • 301
redbeard
  • 21
  • 1
  • 2
  • 7

3 Answers3

2
def first_even(items):
    """ (list of int) -> int

    Return the first even number from items. Return -1 if items     contains no even numbers.

    >>> first_even([1, 9, 3, 77, 13, 5, 8])
    8
    >>> first_even([7, 1])
    -1
    """
    for num in items:
        if num % 2 == 0:
            return num
    return -1
OMRY VOLK
  • 1,429
  • 11
  • 24
1

The else block will be executed for the first odd number, this will terminate the for and discard all previous appends. Instead you want to return the even number as soon as it is found (you don't need the list) and then move the else to align with the for or outside the for:

def first_even(items):
    for num in items:
        if num % 2 == 0:
            return num
    else:
        return -1
    # or put the default value to return here

Reference for Python for/else block:

How can I make sense of the `else` clause of Python loops?

Community
  • 1
  • 1
Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
1

As others have noted, your else case is causing premature termination, and the use of a list is rather pointless. You could fix all this, see the other answers for minimalist fixes, but there is another option: Let Python built-ins do more of the work for you.

The next function will return the first item from an iterator, and with a second argument, if the iterator is empty, it returns the second argument instead. So give it a generator expression that lazily produces all the even numbers in the input, and it will pull the first one and return it. If none are found, the default case will return -1 for you:

def first_even(items):
    # x & 1 == 0 is roughly equivalent and might be slightly faster
    return next((x for x in items if x % 2 == 0), -1)

For the pathological sorts, this might be slightly faster, but it's far less intuitive to people who don't already know Python internals inside and out; I don't recommend it:

from itertools import filterfalse  # ifilterfalse on Py2

def first_even(items):
    # (1).__rand__ is roughly equivalent, and may be faster
    return next(filterfalse((2).__rmod__, items), -1)
ShadowRanger
  • 143,180
  • 12
  • 188
  • 271