Caesar cipher


Here's a 5 minute[1] coding challenge from Programming Praxis:

A Caesar cipher, named after Julius Caesar, who either invented the cipher or was an early user of it, is a simple substitution cipher in which letters are substituted at a fixed distance along the alphabet, which cycles; children’s magic decoder rings implement a caesar cipher. Non-alphabetic characters are passed unchanged. For instance, the plaintext PROGRAMMINGPRAXIS is rendered as the ciphertext SURJUDPPLQJSUDALV with a shift of 3 positions.

-- Source: File:Caesar cipher left shift of 3.svg, public domain

To make it a bit more interesting, I'm actually going to be using a different #lang built on Racket: rackjure. If you're running a newer version of Racket (6+), you can install it with either raco pkg install rackjure or with the Package Manger built into DrRacket. Then just change the #lang line at the top of your file to #lang rackjure.

What specifically do I want from Rackjure? The threading macro ~>. Basically, it takes a value and 'threads' it as the first argument through a series of functions. The example on the Rackjure GitHub page:

> (string->bytes/utf-8 (number->string (bytes-length #"foobar") 16))
#"6"

> (~> #"foobar"
      bytes-length
      (number->string 16)
      string->bytes/utf-8)
#"6"

In our case, we want it because we're going to do run through a similar stream of transformations on each character:

  1. Convert from a character to a number using char->integer
  2. Get to a zero based system by subtracting #\A = 65
  3. Add/subtract the offset for this particular Caesar cipher
  4. Get the Modulo operation so we have a letter when we're done
  5. Get back to a letter by adding #\A = 65 back on
  6. Convert back to a character with integer->char

Turn that into Rackjure:

(define (caesar str n)
  (define A (char->integer #\A))
  (list->string
   (for/list ([c (in-string str)])
     (~> c char->integer (- A) (+ n) (mod 26) (+ A) integer->char))))

If we didn't have ~>, that would look something more like this:

(define (caesar str n)
  (define A (char->integer #\A))
  (list->string
   (for/list ([c (in-string str)])
     (integer->char (+ (mod (+ (- (char->integer c) A) n) 26) A)))))

I'll leave it up to you which of the two styles you think is easier to read--either the Scheme style inside out, jumping back and forth between the operators and their arguments or the Clojure style left to right[2].

Unfortunately, Racket doesn't have a mod function built in[3]. You can get one from R6RS though:

(require (only-in rnrs/base-6 mod))

And there you have it. Simple (and almost trivial to crack) encryption:

> (caesar "HELLOWORLD" 10)
"ROVVYGYBVN"
> (caesar "ROVVYGYBVN" -10)
"HELLOWORLD"

We can make it at least a little better though. Let's go ahead and deal with lower case and non-alphabetic characters:

(define (caesar str n)
  (define A (char->integer #\A))
  (define a (char->integer #\a))
  (list->string
   (for/list ([c (in-string str)])
     (cond
       [(char<=? #\A c #\Z)
        (~> c char->integer (- A) (+ n) (mod 26) (+ A) integer->char)]
       [(char<=? #\a c #\z)
        (~> c char->integer (- a) (+ n) (mod 26) (+ a) integer->char)]
       [else
        c]))))

Basically, the only thing that changes is the offset. For upper case letters, we use #\A = 65, for lower case #\a = 97. Anything that's not a letter? We just leave it alone.

How's it work?

> (caesar "Hello world!" 100)
"Dahhk sknhz!"
> (caesar "Dahhk sknhz!" -100)
"Hello world!"

It's actually such a simple program, you have all of the code right there, but just in case you can also download it from GitHub: caesar-cipher.rkt

  1. More or less
  2. Exercise to the reader: which do you think I prefer?
  3. At least not one that deals how we need with negative numbers
comments powered by Disqus