January 22, 2008 will

Rotonym, what the heck is a rotonym?

Rotonyms are words that ROT-13 in to other words. I discovered the term from this site, which I found during my morning Reddit session. I think the author invented the term, because the only reference I found to 'rotonym' came from his site. Long story short, I knocked out a Python script to find all the rotonyms, given a list of words (just for the heck of it). I like problems like this. Its like the kind of work you had to do in high school and uni, before you enter the real word of programming and discover that most problems have blurry definitions and its hard to tell if your solution actualy works in all cases. Here's the code, let me know if you have a better solution.

words = [line.rstrip() for line in open("WORD.LST")]
words_set = set(words)

letters = "abcdefghijklmnopqrstuvwxyz"
repl = dict(zip(letters, letters[13:]+letters[:13]))

def rot13(word):
    return "".join(repl.get(c, c) for c in word)

found = []

for word in words:
    rotword = rot13(word)
    if rotword in words_set:
        found.append( (word, rotword) )
        words_set.discard(word)

found.sort( key=lambda words:(len(words[0]), words[0]) )
for word, rotword in found:
    print "%s\\t%s"%(word, rotword)

If you want to run it, you will need this word list. Its not the most interesting code you will see, but I was impressed at how much functionality Python squeezes in to each line, without making it too obfusticated. It would be interesting to see this solved in other languages for comparison.

Update: fs111 reminded me that there was a rot13 string encoding in Python. So the code becomes:

words = [line.rstrip() for line in open("WORD.LST")]
words_set = set(words)

found = []

for word in words:
    rotword = word.encode("rot13")
    if rotword in words_set:
        found.append( (word, rotword) )
        words_set.discard(word)

found.sort( key=lambda words:(len(words[0]), words[0]) )
for word, rotword in found:
    print "%s\\t%s"%(word, rotword)
Use Markdown for formatting
*Italic* **Bold** `inline code` Links to [Google](http://www.google.com) > This is a quote > ```python import this ```
your comment will be previewed here
gravatar
fs111

Why that complicated?

>>> w = "Hello"
>>> print w.encode('rot13')
Uryyb

gravatar
Will

Ack. I had forgotten there was a rot13 encoding!

gravatar
Christian Glodt

Why so many loops and so many lines? :D

>>> words = set([w.strip().decode('ascii', 'ignore') for w in file('/usr/share/dict/words')])
>>> print [(w, w.encode('rot13')) for w in words if w.encode('rot13') in words]

First line reads the dictionary while ignoring unicode encoding errors (unfortunately necessary on my Ubuntu system). Second line gets the rotonyms (I don't like the duplicated encode() call in that line).

Favourite rotonyms from my dictionary:
Robyn - Ebola
sync - flap
rail - envy
one - bar
Green - Terra

In conclusion, I

gravatar
Will

In conclusion you what?

Neat, but I also removed equivalent pairs and sorted the output by length of word.

gravatar
fa

Other languages?
here's the stuff in php:
http://stuff.art-core.org/code/rot.phps
http://stuff.art-core.org/code/rot.php

gravatar
Corey

I was thinking something similar to Christian.

words = set([line.strip() for line in file('WORD.LST', 'r')])
rotwords = set([word.encode('rot13') for word in words]) & words
for word in sorted(rotwords, key=len, reverse=True):
print word.encode('rot13'), word

With sorted printing, and maximal work done by native code.

gravatar
Tyler

It should be noted that it is only necessary to run half of the word list (A-M).
I like how Corey sorted by length, it does well to push the one and two letter matches down.

"I think the author invented the term, because the only reference I found to ‘rotonym’ came from his site."

I think I did, also; likely (found and named them) out of the same drive for interesting exercises that motivated you to code your own script to find them.

gravatar
Christian Glodt

I wrote "In conclusion, I <3 (heart) list comprehensions!", but the everything after the < didn't show up because WordPress took it for the beginning of an HTML tag. Sorry about that.

It is nice, every once in a while, to cram as much functionality as possible into the fewest lines of code imaginable.

gravatar
Magnus

An implementation in Haskell: http://therning.org/magnus/archives/337

gravatar
Jeethu Rao

Something similar to Corey and Christian's solutions. The list of words could be a frozen set, because we don't have to mutate it. The 2nd list of rot13 words need not be a set, since we we just iterate over it. This is 2 or 3 lines long, depending on how you look at it :)

s = frozenset(x.strip() for x in open('WORD.LST','r'))
for w, rw in sorted([(x,x.encode('rot13')) for x in s if x.encode('rot13') in s],key=len,reverse=True) : print '%s : %s'%(w.ljust(20),rw)

gravatar
Corey

Jeethu, the change to frozenset is superfluous in this case, it doesn't gain anything. However, all you've managed to do is add back the python-land conditional that Christian's example employed. Using the set intersection (&) to sort out which ROTs are words is much more efficient.