I made a simple ROT13 programme and I don't understand one thing:
a = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
(a.length+1).times do |i|
print a[i + 13]
if i>13
print a[i %14]
end
end
Outputs:
NOPQRSTUVWXYZABCDEFGHIJKLM
If I don't add +1
after a.length
, the iteration ends with the letter L
. However, if I use print a[i]
inside the iteration, it normally starts with A
and ends with Z
with no +1
addition needed.
Can someone explain this mystery for me?
As you may know, the .times
loop invokes the block specified number of times, passing into each iteration an incremented value.
If we say 26.times {|i| puts i}
it will print values from 0 to 25. Up to, but not including the last value.
Now let's walk through the loop. At first iteration, i
is 0. So we print 14th character of the string, "N"
(at index 13, zero-based). We don't go into the condition because 0 is not greater than 13. On second iteration we print 15th character, "O"
. And keep doing this, until we reach i=14
.
At this point, the "magic" begins. First, we attempt to print 27th character of the string. There's no such character so we print literally nothing. Then the condition is triggered and we go in.
i % 14
equals 0, so we print zeroth character, "A"
. Next iteration we print character at index 1 (15 % 14
) and so on, until .times
finishes its iteration and stops calling the block. Now, for this logic to work, the last value for i
must be 26, so that we get 12 in i % 14
and print "M"
.
Length of the entire string is 26. Remember, .times
counts up to but not including the number? That's why we add one to the length, so that it counts from 0 to 26. That's the mystery.
There are many-many ways of improving this code, and you'll learn about them in time. :)
I knew something looked odd about the code. And, of course, there's a bug. When i
is 13 we don't print the first time and we don't go into the condition. We waste one iteration. This is a classic example of "off by 1" class of errors. Here's fixed version of code that doesn't waste iterations and contains no mysteries:
a.length.times do |i|
print a[i + 13]
if i > 12
print a[i % 13]
end
end