2015-09-08 3 views
1

Я взломал простую реализацию связанных списков в Ruby, и я наткнулся на что-то очень своеобразное.Как работает method_name = под капотом?

Я создал класс List с attr_accessor до first_node. Что-то вроде этого:

class List 
    attr_accessor :first_node 

    # Some code here ... 
end 

Затем, при реализации метода delete_at я имел следующую ошибку

linearly.rb:39:in `delete_at': undefined method `next' for nil:NilClass (NoMethodError) from linearly.rb:81:in `<main>'

Это часть метода:

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

Я забыл используйте переменную класса @first_node, и вместо этого я использовал метод чтения first_node. Затем я начал задаваться вопросом, почему first_node возвращает nil при использовании first_node= в той же строке.

Устанавливается ли @first_node в nil перед установкой нового значения?

Обратите внимание, что этот кусок кода работает просто отлично:

def delete_at(position) 
    if position == 0 
    deleted_node = first_node 
    first_node = deleted_node.next 

    return deleted_node 
    else 
    # Some code here ... 

EDIT:

Это, как я называю реализацию:

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) 

ответ

3

Это:

first_node = first_node.next 

создает локальную переменную first_node. Объявления переменных водрузили на вершине сферы в Ruby, так что ваш метод эквивалентен:

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

Это означает, что все first_node ссылки в вашем методе будет локальная first_node переменная, а не геттерных и устанавливающих методы, которые attr_accessor создает для вас.

Либо не использовать переменные, которые соответствуют именам методов или быть явными о том, что вы имеете в виду, снабжая self приемник для вызовов методы:

def delete_at(position) 
    if position == 0 
    deleted_node = self.first_node 
    self.first_node = deleted_node.next 
    #...  
+0

Я не согласен с тем, что эти два метода эквивалентны, но вы были правы в создании локальной переменной. – Dagosi

+1

@Dagosi В чем разница между явным объявлением и инициализацией переменных и возможностью Ruby сделать это за вас? –

+0

@MuIsToShort нет никакой разницы действительно, ну, это зависит от значения, которое вы назначаете при явной инициализации переменных. Это глупый разговор, но я имел в виду, что когда явным образом объявляю 'first_node = nil',' deleted_node = first_node' присваивает 'deleted_node' значение nil.Принимая во внимание, что в моем исходном коде 'deleted_node = first_node' устанавливает' deleted_node' в первый узел в объекте «Список». – Dagosi

0

Вы уверены, что @first_node имеет были установлены? Экземпляры экземпляра возвращают нуль, если они не установлены.

class Test 
    attr_accessor :foo 
    def print_foo 
    puts "@foo=[#{@foo}]" 
    puts "foo=[#{foo}]" 
    puts @foo.nil? 
    end 
end 

t1 = Test.new 
t1.foo # => nil 
t1.print_foo # prints "@foo=[]" and "foo=[]" and returns true 
+0

Да, я настройка 'first_node' перед вызовом' delete_at '. Кажется, я забыл упомянуть об этом. Я добавлю его к описанию. – Dagosi

Смежные вопросы