TL;DR

Here we are with TASK #1 from the Perl Weekly Challenge #097. Enjoy!

The challenge

You are given string $S containing alphabets A..Z only and a number $N. Write a script to encrypt the given string $S using Caesar Cipher with left shift of size $N.

The questions

Well well…

  • regarding the input, what should we do for characters that are not in the alphabeth? Like… spaces?
    • we will map any non-alphabeth character onto itself
  • is the number allowed to be negative or go beyond 26?
    • we will consider a circular approach, using the rest modulo 26

The solution

It’s time for the evil eval! Again! The tr operator is too good to be ignored for this task:

sub caesar_cipher ($S, $N) {
   $N %= 26;
   my $to   = join '', 'A' .. 'Z';
   my $from = substr($to, $N) . substr($to, 0, $N);
   return eval "\$S =~ tr/$from/$to/r";
}

As anticipated, we normalize $N to only be a shift between 0 and 25 (included).

As tr wants a from and a to characters lists, we build them. We’re asked to do a left shift, so it’s easiery to start from the destination $to (that is just the letters in normal alphabetical order) and then build the source $from by taking the last part and putting it into the front.

The rest is a trivial usage of the tr operator, with the same twist that we already saw in previous post PWC090 - DNA Sequence, that is wrapping it inside an evil eval to allow for the source and the destination characters list to be expanded from a variable (they would normally be taken literally).

Don’t you like it? Code it differently! 😉

The whole script, should you be curious:

#!/usr/bin/env perl
use 5.024;
use warnings;
use experimental qw< postderef signatures >;
no warnings qw< experimental::postderef experimental::signatures >;

sub caesar_cipher ($S, $N) {
   $N %= 26;
   my $to   = join '', 'A' .. 'Z';
   my $from = substr($to, $N) . substr($to, 0, $N);
   return eval "\$S =~ tr/$from/$to/r";
}

my $input = shift || 'THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG';
my $n = shift || 3;
say $input;
say caesar_cipher($input, $n);