2013-03-21 3 views
1

У меня есть приложение Ruby, которое я разрабатываю, по какой-то причине не работает должным образом при использовании рекурсивной функции, которая содержит блок внутри, чтобы возвращать значение из другого класса (проще увидеть в примере кода ниже). Странно то, что когда я создал минимальный образец, чтобы попытаться выяснить, что происходит, образец работает так, как ожидалось. Пример:ruby ​​recursive loop с конструкцией возврата субблока

require 'json' 

class Simple 
    attr_accessor :name, :children 

    def initialize(name,children=nil) 
     @name = name 
     @children = children 
    end 
end 

a = Simple.new('A') 
b = Simple.new('B',[a]) 
c = Simple.new('C',[b]) 
d = Simple.new('D') 
e = Simple.new('E',[d]) 
f = Simple.new('F') 
g = Simple.new('G',[e,f]) 

foo = [c,e,g] 

def looper(d) 
    holder = nil 

    d.each do |item| 
     # puts item.name 
     if item.name == 'D' 
      holder = Simple.new('Z',[]) 
     elsif !item.children.nil? 
      holder = looper(item.children) 
     end 
    end 

    return holder 
end 

bar = looper(foo) 
puts "Returned from looper: #{bar.name}" 

В моем фактическом коде я в конечном итоге с помощью переменного экземпляра класса, чтобы получить ответ (который также работает в примере коде). Пример фрагмента функции сверху модифицирована в другой схеме:

def looper(d) 
    holder = nil 

    d.each do |item| 
     # puts item.name 
     if item.name == 'D' 
      @holder = Simple.new('Z',[]) 
     elsif !item.children.nil? 
      looper(item.children) 
     end 
    end 

    @holder 
end 

Так что мой вопрос, является это хорошая практика, чтобы использовать переменную экземпляра? Любые побочные стороны для этого, поскольку он работает для моего фактического кода, тогда как в первом примере шаблона нет?

ответ

0

В своем первом фрагменте кода, я бы ожидать, чтобы увидеть nil от вашего входа, где в вашей второй версии вы получите объект Simple.new('Z',[])

Если это ваша проблема, это потому, что пункт g есть дети, первый будет устанавливать значение возвратно, но второй отключит значение, поэтому второй раз через цикл holder будет установлен в nil.

Редактировать: На самом деле мой анализ результатов из приведенного выше примера неправильный, потому что последний элемент в списке верхнего уровня содержит искомый элемент. Однако анализ проблемы и различие в поведении между двумя решениями все еще выполняется в целом.

Вы, вероятно, просто хотите:

def looper(d) 
    holder = nil 

    d.each do |item| 
     # puts item.name 
     if item.name == 'D' 
      holder = Simple.new('Z',[]) 
      break 
     elsif !item.children.nil? 
      holder = looper(item.children) 
      break if holder 
     end 
    end 

    return holder 
end 

заявление Распад мешает вам снова присваивание holder. , ,

В качестве альтернативы используйте внутренние элементы Ruby, такие как #first или #any?, чтобы выразить ваш поиск.

Назначение переменных экземпляра в соответствии с вашим вторым примером для взаимной связи между рекурсиями - это нормально, они эффективно разделяют между всеми глубинами и итерациями во время выполнения рекурсии. Таким образом, он не разрушает рекурсию или Ruby как таковой, хотя вы должны следить за другими видами нежелательного взаимодействия: например, вы не можете предположить, что переменная экземпляра была установлена ​​в определенном месте во время рекурсии по сравнению с текущей глубиной или элементом цикла. , ,

+0

благодарит за отзыв. На моей Windows 7, установке Ruby 1.9, обе версии возвращают тот же результат. Вы действительно видите различия или просто ожидаете? Это одна из областей Ruby, которую я нахожу наиболее запутанной, потому что то, что я вижу в моем простом примере, фактически не работает в моем полноценном приложении :( – bicarbon8

+0

На самом деле вы правы, я неправильно читаю свой вывод IRB и первый пример возвращает объект, который вы хотите. Однако потенциал для него фактически возвращает «nil», и мое утверждение о необходимости наличия точек останова остается в силе - ваши два примера кода кода ведут себя по-разному из-за возможности «держателя» 'для установки на' nil' после того, как вы нашли значение, поскольку вы продолжаете поиск –

+0

Простейший способ поставить мой последний комментарий - посмотрите на значение looper (foo.reverse) для двух вариантов функции. см. пример того, что происходит в вашем производственном коде, и хороший пример того, почему лучшие тесты включают изменения входных данных. –

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