-4

I'm trying to write a function rot(c,n) that rotates a single character c forward by n spots in the alphabet.

def rot(c,n):
""" rotate c forward by n characters,
    wrapping as needed; only letters change
"""

if 'a' <= c <= 'z':          # lower-case
    new_ord = ord(c) + n
    if new_ord > ord('z'):
        new_ord = new_ord - (2*n)
elif 'A' <= c <= 'Z':        # upper-case
    new_ord = ord(c) + n 
    if new_ord > ord('Z'):
        new_ord = new_ord - (2*n)

else:                        # non-alpha
    new_ord = ord(c)
return chr(new_ord)

However my desired outputs are as follow:

>>> rot('a', 2)
'c'
>>> rot('y', 2)
'a'
>>> rot('A', 3)
'D'
>>> rot('Y', 3)
'B'
>>> rot('!', 4)
'!'

I keep getting the wrong outputs. Can someone tell me what I'm doing wrong?

DSM
  • 342,061
  • 65
  • 592
  • 494
  • 2
    hint: `new_ord = new_ord - (2*n)`. – isedev Oct 02 '14 at 19:40
  • what do you mean by that exactly. – Whooperton Goldberg Oct 02 '14 at 19:41
  • He means, that line is wrong. – Kevin Oct 02 '14 at 19:42
  • come on, try to work it out: given that ord('a') is 97, ord('z') is 122, say n is 2, what would ord('z') + 2 - 2 * 2 give you? considering the expected output is ord('b'), what calculation is actually required? if you want to learn, you've got to go through the hoops. – isedev Oct 02 '14 at 19:44
  • take `rot('y', 2)` for example. Initially, `new_ord` is whatever character is one letter past `z`. The `if new_ord > ord('z'):` line detects this, and says, "subtract 2*n from the value". so the letter ticks back four characters to w. – Kevin Oct 02 '14 at 19:44
  • @WhoopertonGoldberg I think that he was trying to get you to think critically about the problem rather than simply give you the answer. "give a man a fish and you feed him for a day; teach a man to fish and you feed him for a lifetime" – SethMMorton Oct 02 '14 at 20:52

4 Answers4

2

Your problem is here:

new_ord = new_ord - (2*n)

The idea is that when you go past z, you have to remove exactly the whole alphabet and not remove twice what you just added.

Try:

new_ord = new_ord - 26
Ajk_P
  • 1,874
  • 18
  • 23
1

Two liner: (Don't try this at home)

def rot(c, n):
    start = 97 if c.islower() else 65 if c.isupper() else False      
    return chr((ord(c)-start+n)%26 + start) if start else c
kums
  • 2,661
  • 2
  • 13
  • 16
0

This version also supports "negative rotation"; I also switched the conditionals to us .isupper() and .islower(), because I find them slightly more readable (and less prone to off-by-one errors):

def rot(c,n):
    """ rotate c forward by n characters,
        wrapping as needed; only letters change
    """
    new_ord = ord(c) + (n % 26)
    if c.isupper():
        if new_ord > ord('Z'):
            new_ord -= 26
        elif new_ord < ord('A'):
            new_ord += 26
        return chr(new_ord)
    elif c.islower():
        if new_ord > ord('z'):
            new_ord -= 26
        elif new_ord < ord('a'):
            new_ord += 26
        return chr(new_ord)
    return c
TML
  • 12,813
  • 3
  • 38
  • 45
0

Alternativly:

import string
from itertools import cycle

def make_rotation_tables(n):    
    uc, lc = map(cycle, (string.uppercase, string.lowercase))
    nchar = len(string.uppercase)
    part = slice(n, n + nchar)
    rotate = lambda n, it: ''.join(
        [next(it) for i in xrange(26+nchar)][part])
    rot_letters = ''.join(map(lambda x: rotate(n, x), [uc, lc]))    
    return string.maketrans(string.letters, rot_letters)

rottbl = make_rotation_tables(1)
print 'Caesar dixit: "Veni, vidi, vici!"'.translate(rottbl)
>>> Dbftbs ejyju: "Wfoj, wjej, wjdj!"
Don Question
  • 11,227
  • 5
  • 36
  • 54