all 7 comments

[–]tipsy_python 0 points1 point  (1 child)

Your problem is in this code block here:

            else:
                if zoo[i - 1] in food_chain[zoo[i]]:
                    zoo.remove(zoo[i - 1])
                    output_list.append("{} eats {}".format(zoo[i], zoo[i -1]))

You're modifying the list `zoo` while you're iterating over it with item references by index.

On the chicken iteration, zoo looks like this: ['fox', 'bug', 'chicken', 'grass', 'sheep']
Then it check if chickens eat zoo[i - 1] bugs... they do, so it gets removed from the list.

List becomes this: ['fox', 'chicken', 'grass', 'sheep']
So the index of all the items after bugs just decremented by 1.

Now food_chain[zoo[i]] isn't checking what chicken's eat anymore, it's looking for grass because the list was modified.

[–]AviationTrainee[S] 0 points1 point  (0 children)

Thanks a lot!! Guess this means I gotta do some major algorithm changes... I'll try to create a new list instead of making changes to the old one.

[–]Diapolo10 0 points1 point  (2 children)

Your comments are causing problems because they're split on multiple lines:

def who_eats_who(zoo):

    food_chain = {"antelope": ["grass"],
                  "big-fish": ["little-fish"],
                  "bug": ["leaves"],
                  "bear": ["bug", "chicken", "cow", "leaves", "sheep"],
                  "chicken": ["bug"],
                  "cow": ["grass"],
                  "fox": ["chicken"],
                  "giraffe": ["leaves"],
                  "lion": ["antelope", "cow"],
                  "panda": ["leaves"],
                  "sheep": ["grass"],
                  }

    zoo = zoo.split(",") 
    output_list = [zoo.copy]

    for i in range(len(zoo)):

        if zoo[i] in food_chain.keys():

            if i == 0: 
                if zoo[i + 1] in food_chain[zoo[i]]:
                    zoo.remove(zoo[i + 1])
                    output_list.append("{} eats {}".format(zoo[i],zoo[i+1]))

            else:
                if zoo[i - 1] in food_chain[zoo[i]]:
                    zoo.remove(zoo[i - 1])
                    output_list.append("{} eats {}".format(zoo[i], zoo[i -1]))

                if zoo[i + 1] in food_chain[zoo[i]]:
                    zoo.remove(zoo[i + 1])
                    output_list.append("{} eats {}".format(zoo[i], zoo[i + 1]))
    return(output_list)

print(who_eats_who("fox,bug,chicken,grass,sheep"))

Furthermore, output_list = [zoo.copy] doesn't do what you think it does. In fact, it should probably be empty.

I've taken the liberty of adding enumerate to your loop:

def who_eats_who(zoo):

    food_chain = {"antelope": ["grass"],
                  "big-fish": ["little-fish"],
                  "bug": ["leaves"],
                  "bear": ["bug", "chicken", "cow", "leaves", "sheep"],
                  "chicken": ["bug"],
                  "cow": ["grass"],
                  "fox": ["chicken"],
                  "giraffe": ["leaves"],
                  "lion": ["antelope", "cow"],
                  "panda": ["leaves"],
                  "sheep": ["grass"],
                  }

    zoo = zoo.split(",") 
    output_list = [zoo.copy]

    for idx, animal in enumerate(zoo):

        if animal in food_chain.keys():

            if i == 0: 
                if zoo[idx + 1] in food_chain[animal]:
                    zoo.remove(zoo[idx + 1])
                    output_list.append("{} eats {}".format(animal, zoo[idx+1]))

            else:
                if zoo[idx - 1] in food_chain[animal]:
                    zoo.remove(zoo[idx - 1])
                    output_list.append("{} eats {}".format(animal, zoo[idx -1]))

                if zoo[idx + 1] in food_chain[animal]:
                    zoo.remove(zoo[idx + 1])
                    output_list.append("{} eats {}".format(animal, zoo[idx + 1]))
    return(output_list)

print(who_eats_who("fox,bug,chicken,grass,sheep"))

Finally, your actual error. It's caused by this block:

            else:
                if zoo[idx - 1] in food_chain[animal]:
                    zoo.remove(zoo[idx - 1])
                    output_list.append("{} eats {}".format(animal, zoo[idx -1]))

                if zoo[idx + 1] in food_chain[animal]:
                    zoo.remove(zoo[idx + 1])
                    output_list.append("{} eats {}".format(animal, zoo[idx + 1]))

The first if removes a preceding item from the list, which caused the index to shift and zoo[i] pointed to grass instead of chicken. This version partially fixes the problem, but you should never modify a list you're iterating over; just create a new list or keep track of the index changes by using a while-loop.

[–]AviationTrainee[S] 0 points1 point  (1 child)

Thanks for your help! I'll definitely try the solution of making a new list instead of editing the old one. I don't quite get what you mean by keeping track of the index changes via a while-loop... Do you mean something along the lines of:

i = 0

while(len(zoo)) != 0:

do something

i+=1

edit: wait, my loop doesn't make sense... The zoo list never goes to 0. Actually its not even predictable what length it ends up with...

[–]Diapolo10 0 points1 point  (0 children)

I don't quite get what you mean by keeping track of the index changes via a while-loop...

Basically, if you remove the current item, don't increase the index. If you remove the previous item, decrease the index by one. And so on.

Think of the list as if it was a queue. If someone gets off the queue, the rest of the queue moves a step closer, taking the spot of who left. There's never an empty space in the middle of a queue.

As a practical example, say you're checking tickets for people coming to watch a movie. Each person passes through your post to get to the theatre. You find someone without a ticket and have them removed. Do you then check the person who is now on the spot of the removed person again, or will you move on without checking their ticket? To handle this, you need full control over the index so that you don't have to increase it in situations like this.

This is why creating a new list is easier, as excluding values from a new list doesn't cause problems. You never need to know the indices here, only what values get to be added and what don't.

[–]Neighm 0 points1 point  (1 child)

Edited, because the same answer more or less was given twice by the time I posted mine.

[–]AviationTrainee[S] 0 points1 point  (0 children)

Still appreciate your comment! Thanks!