具有子块返回设计的ruby递归循环

时间:2013-03-21 16:59:57

标签: ruby design-patterns recursion

我正在开发一个我正在开发的Ruby应用程序,由于某种原因,当使用包含内部块的递归函数从另一个类的函数调用中返回一个值时,它不会按预期工作(更容易看到)下面的示例代码)。奇怪的是,当我创建一个最小样本试图找出发生了什么时,样本按预期工作。例如:

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

所以我的问题是,使用实例变量是一种好习惯吗?这样做有任何缺点,因为它适用于我的实际代码而第一个示例模式不适用吗?

1 个答案:

答案 0 :(得分:0)

在您的第一段代码中,我希望您的输入中会看到nil,在第二个版本中,您将获得对象Simple.new('Z',[])

如果这是你的问题,那是因为项目g有子项,第一个会设置一个值recusrively,但第二个将取消设置值,所以第二次循环,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

break语句会阻止您再次分配给holder。 。

或者使用像#first#any?这样的Ruby内部来表达您的搜索。

根据您的第二个示例分配实例变量以进行递归之间的相互通信很好,它们在递归运行时实际上是所有深度和迭代之间的共享状态。因此,它不会破坏递归或Ruby本身,尽管您必须注意其他类型的不需要的交互:例如,与当前深度或循环项相比,您不能假设在递归期间在特定位置设置了实例变量。 。