Reputation: 29
This is my code for converting a user entered string into a Caesar cipher.
puts "text?"
text = gets.chomp
puts "key?"
key = gets.chomp.to_i
plainTex = Array.new
ciphTex = Array.new
j = 0
text.each_byte do |i|
plainTex[j] = i
j += 1
end
j = 0
plainTex.each_entry do |i|
if ( i == 32 )
ciphTex[j] = plainTex[j]
j += 1
end
if( plainTex[j] > 64) and (plainTex[j] < 91 )
if( (plainTex[j] + key) > 91)
ciphTex[j] = (plainTex[j] + key ) - 90
j += 1
else
ciphTex[j] = plainTex[j] + key
j += 1
end
end
if( plainTex[j] > 94) and (plainTex[j] < 123)
if( (plainTex [j] + key) > 122)
ciphTex [j] = (plainTex[j] + key) - 122
j += 1
else
ciphTex[j] = plainTex[j] + key
j += 1
end
end
end
ciphTex.each_entry do |i|
puts i
end
now, I'm getting error : undefined method `>' for nil:NilClass (NoMethodError)
Search on the web led me to conclude that plainTex might be nil,as the error message is saying( which should not be the case as plainTex is fed data beforehand).
On a side note : the program runs fine when the input string is all lowercase and has no space. I don't know why.
So, what am I doing wrong?
Upvotes: 2
Views: 1368
Reputation: 1659
When you access array with index that is out of bounds you'll get no error, but result nil
.
irb --simple-prompt
>> a = [1]
=> [1]
>> a[0]
=> 1
>> a[1]
=> nil
>> a[200]
=> nil
A little bit of puts statements reveal the issue:
text.each_byte do |i|
plainTex[j] = i
j += 1
end
j = 0
puts "Length: #{plainTex.length}"
plainTex.each_entry do |i|
if ( i == 32 )
ciphTex[j] = plainTex[j]
j += 1
end
puts "j: #{j}"
This is the output
text?
Testing Test
key?
32
Length: 12
j: 0
j: 2
j: 3
j: 4
j: 5
j: 6
j: 7
j: 8
j: 10
j: 11
j: 12
test.rb:30:in `block in <main>': undefined method `>' for nil:NilClass (NoMethodError)
from test.rb:23:in `each'
from test.rb:23:in `each_entry'
from test.rb:23:in `<main>
Last index of array in this case was 11 and you accessed index 12.
You got result nil
then you tried to call method >
on nil.
You can install gem pry-byebug
and use short aliases s
, n
for step into, next etc.
https://github.com/deivid-rodriguez/pry-byebug
Using pry-byebug
it's immediatelly clear what's the bug, even though I don't really understand your code because those numbers are meaningless to me.
For first letter it increments j
in second condition if( plainTex[j] > 64) and (plainTex[j] < 91 )
. Then with incremented j
it moves to if( plainTex[j] > 94) and (plainTex[j] < 123)
where it increments it again.
You should use elsif
for all of these or next
Ruby convention is to use snake case to name your variable and I'd change this piece of code:
if( plainTex[j] > 64) and (plainTex[j] < 91 )
if( (plainTex[j] + key) > 91)
ciphTex[j] = (plainTex[j] + key ) - 90
j += 1
else
ciphTex[j] = plainTex[j] + key
j += 1
end
end
to this:
if plain_text[j].between?(65, 90)
ciph_text[j] = plain_text[j] + key
ciph_text[j] -= 90 if ciph_text[j] > 91
j += 1
end
It seems that you are repeating j += 1 in every condition, so why not remove it outside if/else blocks and put it as last line before the loop end
?
Also I am a human and I don't want to remember that "A" is 65 and "Z" is 90. Why not say so in the code?
if plain_text[j].between?("A".ord, "Z".ord)
I have never messed with ASCII in ruby, but I am sure you could further improve this, possibly do entire cypher in one simple loop with 10 lines of code or less.
Ruby is really good at processing arrays, so use that. I'd do something like this:
plain_text = text.codepoints
cyph_text = plain_text.map do |code|
if code == ' '.ord
' '.ord
elsif
# return cypher code
end
end
Check out how map function works.
Upvotes: 2