Referencing the second half and excluding the final value of a list

Instructions are given below in the png, and my code is also copy and pasted.

def fashionably_late(arrivals, name):
    """Given an ordered list of arrivals to the party and a name, return whether the guest with that
    name was fashionably late.
    """
    half = len(arrivals)/2
    fashion = arrivals[(int(half) + 1):-1]
    if name in fashion:
        return True
    else:
        return False

I expect return value of True given arrivals=['Paul', 'John', 'Ringo', 'George'] , name='Ringo' , but got False instead.

Where is the logic error in my code?

EDIT: I figured it out, thank you Vaclav and Quercus

def fashionably_late(arrivals, name):
    """Given an ordered list of arrivals to the party and a name, return whether the guest with that
    name was fashionably late.
    """
    if len(arrivals) % 2 != 1:
        half = len(arrivals)/2
        fashion = arrivals[int(half):-1]
        return name in fashion
    else:
        half = len(arrivals)//2
        fashion = arrivals[int(half)+1:-1]
        return name in fashion

Really imaginative exercise :slight_smile:

Suggestions:

  • Insert diagnostic print() calls into your function to get an idea what is going on with the important values.
    • Example: print(half); print(fashion) etc.
  • You may need to split some expressions to smaller ones to get the interesting results for your prints.
  • Hint: This bug is pretty common: Off-by-one error - Wikipedia

Other suggestions:

The expression name in fashion already has a boolean result (True|False) so instead of:

    if name in fashion:
        return True
    else:
        return False

You can use much simpler:

    return name in fashion

The expression len(arrivals)/2 has result of type float which you later convert to int. You can use integer division and avoid later conversions: len(arrivals) // 2. The result will not be an exact half anymore so maybe the variable name should be updated then.

3 Likes

It is a good idea to use integer division to help divide the arrivals list. Consider the above expression in the context of a list that has an odd number of items compared to what happens with a list that has an even number of items. Is there a difference? It may also be worthwhile to think about whether the expression (len(arrivals) + 1) // 2 might be useful for this calculation.

With the right expression for calculating the index of the first person who arrived after at least half of the party’s guests, you can simply your code, eliminating the need for an if and an else block. That expression would compute the index correctly, regardless of whether an even or odd number of guests attended. See if you can figure it out. We can provide assistance if you need it.

2 Likes

It looks like you gave up looking for a simpler solution. I have already written it four days ago so here it is:

def fashionable_arrivals(arrivals):
    """Get list of guests who arrived fashionably late."""
    after_half = (len(arrivals) + 1) // 2  # the first index after the half
    return arrivals[after_half:-1]

def fashionably_late(arrivals, guest):
    """Tell if the guest is fashionably late.
    
    Given an ordered list of arrivals to the party and a name, return whether
    the guest with that name was fashionably late.
    """
    return guest in fashionable_arrivals(arrivals)
arrivals = ['Paul', 'John', 'Ringo', 'George']
fashionably_late(arrivals, 'Ringo')  # True

Of course you can merge the two functions into one but having them separate makes the testing easier. See the testing in a Jupyter notebook: python-ntb/2022-07-10_fashionable_arrival.ipynb at main · vbrozik/python-ntb · GitHub

1 Like

Thanks, @vbrozik. That’s the key concept regarding how we can use a modulus along with an addend to get to the appropriate index for both an odd or even number of items in the list.

1 Like

Glad you found a workable solution, @ThomasVenner.

Regarding the bug in the original code, did you notice what index is produced by (int(half) + 1) compared to the list indexes?  (The int() will work.)

0: 'Paul'
1: 'John'
2: 'Ringo'
3: 'George'

It looks like you ran into the 0-indexing paradox. It’s extremely useful most of the time, but at times like this, we end up either 1 before or 1 after where we wanted to be (and thought we were). I read recently that Julia and a few other languages use 1-based indexing and it creates more problems that it solves.

1 Like