undefined method `<' for nil:NilClass error but no nil exists? - ruby

Was wondering why I get the error: "undefined method `<' for nil:NilClass" when compiling. After looking for reasons why, I found that you cannot use [] on an object with nil as a value. This makes sense, but I don't see why my array would contain nil in it. What am I missing?
def binary_search(n, arr)
middle = arr.length #pick middle value
i = 0
j = arr.length - 1
while i <= j
if arr[middle] == n
return true
elsif arr[middle] < n
i = middle + 1
middle = (i + j) / 2
else
j = middle - 1
middle = (i + j) / 2
end
end
false
end
nums = [76,32,50,90,10,8,15,49]
nums.sort
puts nums.inspect
binary_search(50, nums)

Let's look at a simplified subset of the code:
arr = [76,32,50,90,10,8,15,49]
middle = arr.length # 8
arr[middle] < 50 # NoMethodError
The length is 8.
arr[8] is nil, because there is no item at index 8. Remember that Ruby indexes begin with 0.
nil < 50 is a NoMethodError

Related

Ruby: undefined method '>' for nil:NilClass (NoMethodError)

Currently working on a binary search algorithm for Ruby, but am running into an error when comparing the middle array element with n.
Code Below:
def b_search(n, arr)
middle = arr.length / 2
first = 0
last = arr.length - 1
while first <= last
middle = first + last / 2
if arr[middle] == n
return true
elsif arr[middle] > n
last = middle - 1
else
first = middle + 1
end
end
false
end
nums = [1, 2, 3, 4, 5]
target = 4
if b_search(target, nums)
puts "Target Found"
else
puts "Target Not Found"
end
The error evidently happens in the while loop at the
elsif arr[middle] > n
line, but I'm not sure why. Any help would be appreciated.
It occurs because arr[middle] is nil and nil has no method > defined on it.
Consider that
middle = first + last / 2
is equals to
middle = first + (last / 2)
and you probably meant
middle = (first + last) / 2
so use the parentheses because / has a higher precedence

recursive binary search in ruby

I've been learning some algorithms and I can't find the reason why my method is failing. if you could look at the code and shed some light as to why that is happening. I would truly appreciate it.
I'm trying to write a method that would binary search an array recursively and so far that is all my code.
def recursive_binary_search(arr, target)
max_index = arr.length - 1
mid_index = max_index / 2
if arr[mid_index] > target
new_arr = arr[0..(mid_index - 1)]
recursive_binary_search(new_arr, target)
elsif arr[mid_index] < target
new_arr = arr[(mid_index + 1)..max_index]
recursive_binary_search(new_arr, target)
else
return mid_index
end
end
The error I keep getting is undefined method '>' for nil:NilClass
I was unable to reproduce the exception reported by the OP (as the data that produced the exception was not given in the question), but the main problem is that, because max_index is computed from arr, and arr is constantly getting smaller, the index returned by the method will have no relation to the correct index in the initial array arr.
Suppose, for example, that arr = [1,2,3,4,5,6] and target = 6. In this case the method will return 0 (rather than 5) as the index of the target element. That's because arr will progressively become arr[3..6], arr[4..6], arr[5..6] and arr[6], at which point index 0 will be returned.
Here is one way the method could be written, using a case statement. The method assumes that target is an element of arr and (as required by binary searches) the elements of arr are ordered, smallest to largest.
def recursive_binary_search(arr, target, min_index=0, max_index=arr.size-1)
mid_index = (min_index+max_index)/2
case arr[mid_index] <=> target
when 0 # arr[mid_index] == target
mid_index
when -1 # arr[mid_index] < target
min_index = mid_index + 1
recursive_binary_search(arr, target, min_index, max_index)
when 1 # arr[mid_index] > target
max_index = mid_index - 1
recursive_binary_search(arr, target, min_index, max_index)
end
end
arr = [1,2,3,4,5,6]
arr.each { |target| puts "#{target}: #{recursive_binary_search(arr, target)}" }
1: 0
2: 1
3: 2
4: 3
5: 4
6: 5
If your arrays are sorted you could try something like this:
def search(arr, target)
return nil if array.empty?
mid_index = array.length / 2
case target <=> array[mid_index]
when -1
search(array.take(mid_index), target)
when 0
mid_index
when 1
subs = search(array.drop(mid_index + 1), target)
subs.nil? ? nil : (mid_index + 1) + subs
end
end

'undefined method `>' for nil:NilClass in ruby

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?
Problem Explained
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
Debugging your code
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.
Professional debugging
You can install gem pry-byebug and use short aliases s, n for step into, next etc.
https://github.com/deivid-rodriguez/pry-byebug
Bug Found
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
Code Cleanup
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.

Nuances of where to define a variable in ruby code

I've just started learning ruby, and the position of where variables are defined somewhat elude me. For example, why does this code work:
def two_sum(nums)
result = nil
i = 0
while i < nums.length
k = (nums.length - 1)
if nums[i] + nums[k] == 0
result = [i,k]
end
i += 1
k -= 1
end
return result
end
And why does this code not work:
def two_sum(nums)
result = nil
i = 0
k = (nums.length - 1)
while i < nums.length
if nums[i] + nums[k] == 0
result = [i,k]
end
i += 1
k -= 1
end
return result
end
Thank you in advance!
I think you code might just have a bug
while i < nums.length
k = (nums.length - 1)
...
k -= 1 # this statement has no effect!
end
Above, the value if k is always (nums.length - 1) because you reassign it at the begin of each iteration. The other statement has no effect.
k = (nums.length - 1)
while i < nums.length
...
k -= 1
end
Above, the value of k starts at (nums.length - 1) in the first iteration and is then reduced by 1 for each iteration.
Pro tipp —
It is very unusual in Ruby to use a for/while/until loop. If you want to loop over all elements use each or each_with_index instead
array.each { |each| ... }
array.each_with_index { |each, n| ... }

Getting a 'nil:Nil Class' error in Ruby, but the array doesn't seem to empty

I'm trying to code the 'Sieve of Eratosthenes' in Ruby and I'm having difficulty in the second 'while' loop. I want to test to see if integers[j] % integers[0] == 0, but the compiler keeps giving me a nil:Nil Class error at this line. I can't figure out the problem.
n = gets.chomp.to_i
puts
while n < 2
puts 'Please enter an integer >= 2.'
puts
n = gets.chomp.to_i
puts
end
integers = []
i = 0
while i <= n - 3
integers[i] = i + 2
i += 1
end
primes = []
j = 1
while integers != []
primes.push integers[0]
while j <= integers.length
if integers[j] % integers[0] == 0
integers.delete(integers[j])
end
j += 1
end
integers.shift
j = 1
end
puts integers
puts
puts primes
Thanks in advance for any help!
It's an off-by-one error. You're testing for j <= integers.length. So, for example, if you array has five items, the last iteration will be integers[5]. But the last index in a five-item array is 4 (because it starts at 0). You want j < integers.length.

Resources