问题描述:

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
```

The length of the string of letters is 26, however the index is 0 based. With this being the case, the letter Z is index number 25. The times method will not run the final iteration(26). Therefore to account for that, we add a +1 to the length.