Caesar Cipher in Ruby
From Wikipedia:
In cryptography, a Caesar cipher, also known as Caesar’s cipher, the shift cipher, Caesar’s code or Caesar shift, is one of the simplest and most widely known encryption techniques. It is a type of substitution cipher in which each letter in the plaintext is replaced by a letter some fixed number of positions down the alphabet. For example, with a left shift of 3, D would be replaced by A, E would become B, and so on. The method is named after Julius Caesar, who used it in his private correspondence.
There’s a brief video about it from Harvard’s CS50 class.
Breaking down the following solution line by line:
1 puts "Word please: " 2 text = gets.chomp.downcase 3 4 puts "Number please: " 5 n = gets.chomp.to_i 6 7 def caesar_cipher(text, n) 8 alphabet = ('a'..'z').to_a 9 key = Hash[alphabet.zip(alphabet.rotate(n))] 10 text.each_char.inject("") { |newtext, char| newtext + key[char] } 11 end 12 13 puts caesar_cipher(text, n)
Line 1:
puts "Word please: "
Prompts the user to write a word.
Line 2:
text = gets.chomp.downcase
gets
takes the word the user writes and .chomp
cuts off the end parameters (like “/n”). The new word is now referred to as ‘text’.
Line 4:
puts "Number please: "
Prompts the user to write a number.
Line 5:
n = gets.chomp.to_i
gets
takes the input number (which is a string)
.chomp
cuts the end parameters off, and then .to_i
turns the number into an integer.
The new number is now referred to as ‘n’.
def caesar_cipher(text, n)
def caesar_cipher(text, n)
defines a new method called caesar_cipher
that takes in two arguments text
and n
which are taken from Line 2 and Line 4 respectively.
alphabet = ('a'..'z').to_a
Defines a new variable called alphabet
that is equal to individual letters a
all the way to z
. These letters are each their own individual string. The .to_a
turns the 26 letters into an array. This array is named alphabet
.
key = Hash[alphabet.zip(alphabet.rotate(n))]
A new variable key
is defined. It is equal to Hash[alphabet.zip(alphabet.rotate(n))]
Let’s break it down.
alphabet.rotate(n)
– What does this do?
.rotate
is a method in Ruby that returns a new array by rotating the array elements.
For example:
a = [ "a", "b", "c", "d" ] a.rotate #=> ["b", "c", "d", "a"] a #=> ["a", "b", "c", "d"] a.rotate(2) #=> ["c", "d", "a", "b"] a.rotate(-3) #=> ["b", "c", "d", "a"]
In this case, alphabet.rotate(n)
takes the alphabet
variable (the array of all 26 letters) and rotates the array n
number of times. The n
comes from what the user puts when prompted in the beginning (see Line 5).
alphabet.rotate(n)
is inside alphabet.zip()
. What does alphabet.zip()
do?
.zip
is a useful method for combining collections in an ordered way.
[1,2,3].zip(['a', 'b', 'c']) #=> [[1, "a"], [2, "b"], [3, "c"]]
As you can see, the above two arrays are combined together so that each number is paired with each letter. The number 1 is associated with ‘a’ and so forth.
.zip
can also take 2 (or more) arguments to zip into the first:
['a', 'b', 'c'].zip( [1,2,3], ['oogie', 'boogie', 'booger'] ) #=> [["a", 1, "oogie"], ["b", 2, "boogie"], ["c", 3, "booger"]]
So going back to our code, alphabet.zip(alphabet.rotate(n))
is taking our rotated alphabet variable array and matching the new order with our original alphabet array. For example, if n
was 2, our alphabet variable would match with the new rotated alphabet like so:
n = 2 alphabet = ['a', 'b', 'c', 'd', 'e'..'z'] new_alphabet = ['c', 'd', 'e', 'f', 'g'..'b'] // new_alphabet = alphabet.rotate(2) alphabet.zip(new_alphabet) = ['a', 'c'],['b', 'd'], ['c', 'e'] ..['z', 'b']
Hash[alphabet.zip(alphabet.rotate(n))]
Hash
turns each pairing into key => value
pairings.
For example, alphabet.zip(new_alphabet)
would be equal to [['a' => 'c'], ['b' => 'd'], ['c' => 'e']..['z' => 'b']]
. So now the value for ‘a’ is ‘c’, and so on.
Line 10:
Let’s break down this, too:
text.each_char.inject("") { |newtext, char| newtext + key[char] }
What exactly is this line of code doing??
text
text
takes the string entered by the user (See Line 2).
text.each_char
each_char
takes the string entered by the user (text
) and separates the string into their separate characters. For example, if text
were “apple”, each_char
would make “apple” turn into “a”, “p”, “p”, “l”, “e”.
text.each_char.inject("")
.inject("")
sends each character through the block (the block being everything between the brackets {}). Each character of text passes through the block in the argument place labeled char
.
The ("")
defines the default starting point. It means the first time we pass through the block, we are defaulting (starting) with an empty string (“”).
{ |newtext, char| newtext + key[char] }
This is a block. It is connected to the .inject
enumerator method, which just means every time you use .inject
, you will have a block following it. There are two arguments in this block ‘newtext
‘ and ‘char
‘ that are new.
newtext
is the default (starting) string, which in the first go through, would be an empty string (“”) because that’s what we declared after the .inject
method: text.each_char.inject("")
char
is the individual characters from the text
variable.
key[char]
takes in the individual character [char]
and assigns its value found from the key
variable we defined in Line 9.
All the individual characters will be run through the block until there are no more characters in the text
string. To see how this works, let’s use the example of “apple”.
text.each_char.inject("") { |newtext, char| newtext + key[char] } text = "apple" n = "2" first run through => "apple".each_char = "a", "p", "p", "l", "e" ("a", "p", "p", "l", "e").inject("") { |"", "a"| "" + key["a"] } key["a"] == "c" 1st ("a", "p", "p", "l", "e").inject("") { |"", "a"| "" + "c" } 2nd ("p", "p", "l", "e").inject("c") { |"c", "p"| "c" + [key["p"]=="r"] } 3rd ("p", "l", "e").inject("r") { |"r", "p"| "cr" + "r" } 4th ("l", "e").inject("r") { |"r", "l"| "crr" + "n" } 5th ("e").inject("n") { |"n", "e"| "crrn" + "g" } "crrng"
Line 11:
end
end
is connected to our method caesar_cipher
and means that we are done defining the method.
Line 13:
puts caesar_cipher(text, n)
This writes the result of the caesar_cipher
method for us.
Project located here: http://www.theodinproject.com/ruby-programming/building-blocks?ref=lnav
Rotate method: http://apidock.com/ruby/Array/rotate
Method arguments: http://www.skorks.com/2009/08/method-arguments-in-ruby/
Zip method: http://dfriedm.github.io/blog/2013/10/12/ruby-zip-method/
Hash: http://ruby-doc.org/core-1.9.3/Hash.html
Each_char: http://ruby-doc.org/core-2.1.0/String.html#method-i-each_char
Inject: http://ruby-doc.org/core-2.2.2/Enumerable.html#method-i-inject
https://blog.udemy.com/ruby-inject/
As someone putting a lot of effort into learning the standard Ruby library so that I can apply it to solutions like the Caesar Cipher, this is really helpful. The one thing that this solution doesn’t do, however, is address phrases. For a single word it works great. For a phrase with spaces between words it fails. This gives me a good starting point but I’m going to have to figure out logic to handle phrases and even upper vs lower case. Good post, though…again, really helpful!
LikeLike
Thanks for your comment, Martin. I hadn’t thought about also implementing for phrases and upper & lower cases. I’ll also work on it and see if I find a solution. Thanks for visiting!
LikeLike
Thanks mate. Was scratching my head at getting this to work. You made it relatively straight forward and easy to understand.
Much obliged.
Curtis
LikeLike
Glad it helped! Thanks for visiting!
LikeLike