Question

I'm writing a ROT13 function, and I can't understand why the following doesn't work:

def ROT(string):
    # y = 0
    for char in string:
        x = ord(char)
        if 97 <= x < 110 or 65 <= x < 78:
            # string[y]=char.replace(char, chr(x+13))
            char=char.replace(char, chr(x+13))
            print(char)
            # y+=1
        elif x >= 110 or 78 <= x < 91:
            # string[y]=char.replace(char, chr(x-13))
            char=char.replace(char, chr(x-13))
            print(char)
            # y+=1
    return string

string = ROT('Hello, lorem ipsum dolor sit amet')
print(string)

The call to the function just prints the original string. As you can see in the commented lines above (apologies if it's a little hard to read), I tried defining a variable y to increment through the string, and then access it but I get a runtime error. The solution I came up with was to create an empty string at the beginning of the function (and from my googling that seems to be the solution most people use), but no one explains why that is. Why doesn't it work to return the original string if you're replacing every character in it?

Was it helpful?

Solution

The problem in your code is you are not manipulating the original string. You are just replacing the temporary variable char and not in the original string. Since strings are immutable in python, you may try using a new string and instead of replacing the original string, you may just append the characters to the new string. Like:

modified_string = ""
for char in string:
     #whatever condition
     modified_string += #value to be added

OTHER TIPS

You were returning the original string, try

def ROT(string):
#   y = 0
    result = ""
    for char in string:
        x = ord(char)
        if 97 <= x < 110 or 65 <= x < 78:
#           string[y]=char.replace(char, chr(x+13))
            char=char.replace(char, chr(x+13))
            result = result + char
            print(char)
            continue
#           y+=1
        elif x >= 110 or 78 <= x < 91:
#           string[y]=char.replace(char, chr(x-13))
            char=char.replace(char, chr(x-13))
            print(char)
            result = result + char
            continue
#           y+=1
        result = result + char
    return result

string = ROT('Hello, lorem ipsum dolor sit amet')
print(string)

Strings in Python are immutable.

Others have already addressed the main problem -- strings are immutable, so you can't just switch a single character while you're iterating. You could use a bytearray instead, but...

FWIW, this is a good candidate for string.translate:

>>> import string
>>> fromchr = ''.join(chr(x) for x in range(97, 110) + range(65, 78))
>>> tochr = ''.join(chr(x+13) for x in range(97, 110) + range(65, 78))
>>> fromchr += ''.join(chr(x) for x in range(110, 256) + range(78, 91))
>>> tochr += ''.join(chr(x-13) for x in range(110, 256) + range(78, 91))
>>> trans = string.maketrans(fromchr, tochr)
>>> 'Hello, lorem ipsum dolor sit amet'.translate(trans)
'Uryyb, yberz vcfhz qbybe fvg nzrg'

The great thing here is that creating the translation table is a 1-time cost. After the translation table is created, you can use it as many times as you want. Your translation will happen in ~O(n) time in optimized C code, so I would be surprised if you could get an implementation that is much faster (or simpler really).

This way even handily beats the builtin 'rot13' codec:

def rot13a(s):
  return s.encode('rot13')

import string
fromchr = ''.join([chr(x) for x in range(97, 110) + range(65, 78)])
tochr = ''.join([chr(x+13) for x in range(97, 110) + range(65, 78)])
fromchr += ''.join(chr(x) for x in range(110, 256) + range(78, 91))
tochr += ''.join(chr(x-13) for x in range(110, 256) + range(78, 91))
trans = string.maketrans(fromchr, tochr)
def rot13b(s):
  return s.translate(trans)

import timeit
test_string = 'Hello, lorem ipsum dolor sit amet'
print rot13a(test_string) == rot13b(test_string)
print timeit.timeit("rot13a(test_string)", "from __main__ import test_string, rot13a")
print timeit.timeit("rot13b(test_string)", "from __main__ import test_string, rot13b")

(my results):

True
1.52055001259  # rot13a
0.21444106102  # rot13b

Note that this is python2.x code. In python3.x, you can't just add the ranges like that since range no longer returns a list object. But, hopefully the idea is clear enough...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top