Caesar cipher

Here’s a 5 minute1 coding challenge from Programming Praxis:

A caeser 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: Wikipedia, 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 modulus 26 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 right2.

Unfortunately, Racket doesn’t have a mod function built in3. 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 []
All
By category

Leave a Reply