I was hacking a simple implementation of linked lists in Ruby and I came across something very peculiar.
I created a List
class with an attr_accessor
to a first_node
. Something like this:
class List
attr_accessor :first_node
# Some code here ...
end
Then, while implementing the method delete_at
I had the following error
linearly.rb:39:in `delete_at': undefined method `next' for nil:NilClass (NoMethodError)
from linearly.rb:81:in `<main>'
This is a piece of the method:
def delete_at(position)
if position == 0
deleted_node = first_node
first_node = first_node.next # This is line 39.
return deleted_node
else
# More code here ...
I forgot to use the class variable @first_node
and instead I used the reader method first_node
. Then, I started wondering why first_node
returns nil
when using first_node=
in the same line.
Does it set @first_node
to nil
before setting a new value?
Notice that this piece of code works just fine:
def delete_at(position)
if position == 0
deleted_node = first_node
first_node = deleted_node.next
return deleted_node
else
# Some code here ...
EDIT:
This is how I call the implementation:
list = List.new
list.first_node = Node.new(1)
list.first_node.next = Node.new(2)
list.first_node.next.next = Node.new(3)
puts "Delete at 0"
puts list.delete_at(0)
This:
first_node = first_node.next
creates a local variable called first_node
. Variable declarations are hoisted to the top of the scope in Ruby so your method is equivalent to:
def delete_at(position)
deleted_node = nil # Declare deleted_node and first_node
first_node = nil # as local variables.
if position == 0
deleted_node = first_node
first_node = deleted_node.next
return deleted_node
else
# Some code here ...
That means that all first_node
references in your method will be the local first_node
variable rather than the getter and setting methods that attr_accessor
creates for you.
Either don't use variables that match method names or be explicit about what you mean by supplying the self
receiver for the method calls:
def delete_at(position)
if position == 0
deleted_node = self.first_node
self.first_node = deleted_node.next
#...