Why does my code only work for low character strings?


#1

<PLEASE USE THE FOLLOWING TEMPLATE TO HELP YOU CREATE A GREAT POST!>

<Below this line, add a link to the EXACT exercise that you are stuck at.>
https://www.codecademy.com/courses/learn-python/lessons/practice-makes-perfect/exercises/reverse?action=lesson_resume

<In what way does your code behave incorrectly? Include ALL error messages.>
Your function fails on reverse(“coolstory”). It returns “yroootslc” when it should return “yrotslooc”.

```python

text = “101010101010101010101010101”

def reverse(text):
words = []
reverse_words = []
for word in text:
words.append(word)
while len(words) > 0:
reverse_words.append(words[len(words) - 1])
words.remove(words[len(words) - 1])
return “”.join(reverse_words)

print reverse(text)

<do not remove the three backticks above>

#2

When things are not working the first thing I look for in the code is an improperly understood (meaning incorrectly implemented) built-in method; and, sure enough, there it is.

Let’s go to the top of the code and work down from there…

That is a palindrome, not much to see as it looks the same before and after. Not sure what you expected, but no harm, no foul.

def reverse(text):    # check

words = []    # here I have a problem

That it is a list, not so much, but what you chose to call it. words. We are dealing with a single word or phrase, the emphasis on singular. There are no words in this equation, only characters in a string. We do not need to transpose the string over to a list, just iterate over it. But, a list in this case will be the approach, which is fine.

reverse_words = []

Again, poor naming. We want to tell the reader something that is truthful and meaningful.

reversed_text

makes perfect sense.since it tells the reader exactly what it is.

for word in text:

Once again, we are not iterating words, but characters.

for letter in text:

or

for char in text:

are more truthful and descriptive of what they actually are.

words.append(word)

We already determined that this list was not needed (so neither is the previous line, also). However, we will use a chars list in the following discussion, so we haven’t tossed it out.

while len(words) > 0:

Okay, this tells the reader that the list is being truncated on each pass. But, is it really being truncated?

# note the immediate conversion to a list, no loop involved

>>> def reverse(text):
    chars = list(text)
    reversed_text = []
    while len(chars) > 0:
        print(chars[len(chars) - 1])
        reversed_text.append(chars[len(chars) - 1])
        print(reversed_text)
        chars.remove(chars[len(chars) - 1])
        print (chars)
    return "".join(reversed_text)
>>> reverse("coolstory")
y
['y']
['c', 'o', 'o', 'l', 's', 't', 'o', 'r']
r
['y', 'r']
['c', 'o', 'o', 'l', 's', 't', 'o']
o
['y', 'r', 'o']
['c', 'o', 'l', 's', 't', 'o']
o
['y', 'r', 'o', 'o']
['c', 'l', 's', 't', 'o']
o
['y', 'r', 'o', 'o', 'o']
['c', 'l', 's', 't']
t
['y', 'r', 'o', 'o', 'o', 't']
['c', 'l', 's']
s
['y', 'r', 'o', 'o', 'o', 't', 's']
['c', 'l']
l
['y', 'r', 'o', 'o', 'o', 't', 's', 'l']
['c']
c
['y', 'r', 'o', 'o', 'o', 't', 's', 'l', 'c']
[]
'yroootslc'
>>> 

So what is happening here, really?

Before we get to the heart of the problem, let’s first refactor this,

reversed_text.append(chars[len(chars) - 1])

An index works in both directions. [0] is the first index, [-1] is the last. So the line becomes,

reversed_text.append(chars[-1])
>>> def reverse(text):
    chars = list(text)
    reversed_text = []
    while len(chars) > 0:
        print(chars[-1])
        reversed_text.append(chars[-1])
        print(reversed_text)
        chars.remove(chars[-1])
        print (chars)
    return "".join(reversed_text)

That makes for some easier reading, and frees us up to focus on the main issue.


Earlier I spoke of an incorrectly implemented built-in. From the above we can see that remove does indeed a remove a character (value), the first one it encounters in the list moving from left to right. The o at the end is not the one being removed. The one closest to the beginning of the list is captured. Think you can solve this, now?

Hint
>>> def reverse(text):
    chars = list(text)
    reversed_text = []
    while len(chars) > 0:
        print(chars[-1])
        reversed_text.append(chars.pop())
        print(reversed_text)
        #chars.remove(chars[-1])
        print (chars)
    return "".join(reversed_text)
>>> reverse("coolstory")
y
['y']
['c', 'o', 'o', 'l', 's', 't', 'o', 'r']
r
['y', 'r']
['c', 'o', 'o', 'l', 's', 't', 'o']
o
['y', 'r', 'o']
['c', 'o', 'o', 'l', 's', 't']
t
['y', 'r', 'o', 't']
['c', 'o', 'o', 'l', 's']
s
['y', 'r', 'o', 't', 's']
['c', 'o', 'o', 'l']
l
['y', 'r', 'o', 't', 's', 'l']
['c', 'o', 'o']
o
['y', 'r', 'o', 't', 's', 'l', 'o']
['c', 'o']
o
['y', 'r', 'o', 't', 's', 'l', 'o', 'o']
['c']
c
['y', 'r', 'o', 't', 's', 'l', 'o', 'o', 'c']
[]
'yrotslooc'
>>> 

#3

Wow thank you for that! I used your code and, yes it worked. I tried to do it without looking at the hint but I couldn’t understand why the .remove(chars[-1]) function didnt always remove the last character, and why sometimes it decided to remove the second character from the beginning?

Also, how does the code reader know that the chars.pop() function means to remove and return the last character of chars? For instance, when I tried chars.pop(chars[-1]) it returned an error message.


#4

pop() is an index based function. We tell it which index to remove and it removes that index. When no index is provided in the argument, JS defaults to index[-1] (the last one).

chars[-1]

is a value, not an index.

One thing that pop() does that del() and remove() do not is make a copy before removing. That is why we could assign it as the argument to append(). You may also see it written as follows:

last_one = list.pop()    # list.pop(-1) returns the same thing

first_one = list.pop(0)

middle_one = list.pop(int(len(list) / 2))    # assumes an odd length list

In the above I use list as a placeholder for a list name. We should never name our objects using reserved words.


>>> my_list = [2,5,9,14,20]
>>> middle = my_list.pop(int(len(my_list) / 2))
>>> middle
9
>>> my_list.insert(int(len(my_list) / 2), middle)
>>> my_list
[2, 5, 9, 14, 20]
>>> 

Now try this on an even length list…

>>> my_list = [2,5,9,14,20,27]
>>> middle = my_list.pop(int(len(my_list) / 2))
>>> middle
14
>>> my_list.insert(int(len(my_list) / 2), middle)
>>> my_list
[2, 5, 14, 9, 20, 27]
>>> 

#5

Aside with some more siliiness…

>>> my_list = [2,5,9,14,20,27]
>>> m1 = my_list.pop(int(len(my_list) / 2))
>>> my_list.insert(int(len(my_list) / 2), m1)
>>> m2 = my_list.pop(int(len(my_list) / 2))
>>> m1 if m1 == m2 else float(m1 + m2) / 2
11.5
>>> my_list = [2,5,9,14,20]
>>> m1 = my_list.pop(int(len(my_list) / 2))
>>> my_list.insert(int(len(my_list) / 2), m1)
>>> m2 = my_list.pop(int(len(my_list) / 2))
>>> m1 if m1 == m2 else float(m1 + m2) / 2
9
>>> 

>>> my_list = [2,5,9,14,20,27]
>>> m = int(len(my_list) /2)
>>> m1 = my_list.pop(m)
>>> my_list.insert(m - 1, m1)
>>> m2 = my_list.pop(m)
>>> m1 if m1 == m2 else float(m1 + m2) / 2
11.5
>>> my_list = [2,5,9,14,20]
>>> m = int(len(my_list) /2)
>>> m1 = my_list.pop(m)
>>> my_list.insert(m, m1)
>>> m2 = my_list.pop(m)
>>> m1 if m1 == m2 else float(m1 + m2) / 2
9
>>> 

#6

Ok I think I followed all of those ways to use the pop() and insert() functions, but do you know why in my original code the .remove() function removed the last letter as intended but then after two loops removed the letter that was 2 from the beginning?


#7

It didn’t remove the last o, but the first one from the left, so the last one was still there on the next iteration. remove() then took out the second o with there still being an o at the end. On the third iteration, the last o was the only one in the string, so it fianlly got removed.

['c', 'o', 'o', 'l', 's', 't', 'o']  <<< last o
o
['y', 'r', 'o']
['c', 'o', 'l', 's', 't', 'o']  <<< still there
o
['y', 'r', 'o', 'o']
['c', 'l', 's', 't', 'o']  <<< still there
o
['y', 'r', 'o', 'o', 'o']
['c', 'l', 's', 't']  <<< finally removed

#8

But WHY does it do that. It should have removed the last letter everytime, but it didn’t. I’m wondering why?


#9

Because that is not how remove() works. We give it a value, and it removes the first instance of that value that it finds, seeking from left to right.

In my example above, I used pop() because it takes an index so we can zero in on the physical element, without regard for the value it holds.


#10

Ok, but in my original code the value I gave it was (len(words) - 1) (if you can call that a value). So why was it so fixed on removing “o”, where did it get the idea to remove “o” specifically?


#11

That will be a value, not an index.


#12

Can I infer then, that because I gave it an incorrect index, the code randomly removed the last two letters, then removed the first “o” 's, then continued to remove the last letters. To me there is no explanation to this behaviour. Are you able to explain this behaviour?


#13

Recall that remove does not take an index. There was nothing incorrect since you gave it a value (word[…]). When the value was y it was found at the end because it was the only y. When the value was r it too was found at the end because it was the only r. When the value as o, it was found right after the c and removed from that position. The next o was also found after the c. Only when there was one o left in the text was it found in the end position.

I have, at least three times already in this thread. Your mind is tricking you into believing that somehow remove should take an index. It doesn’t and it won’t.


#14

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.