1

我有一个我正在开发的 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

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

4

1 回答 1

0

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

如果这是您的问题,那是因为 itemg有子项,第一个将重新设置一个值,但第二个将取消设置该值,因此第二次通过循环,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. . .

或者使用 Ruby 内部结构,如#first#any?来表达您的搜索。

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

于 2013-03-21T17:24:39.803 回答