我可能会这样写:
class Object
def all_variables(root=true)
vars = {}
self.instance_variables.each do |var|
ivar = self.instance_variable_get(var)
vars[var] = [ivar, ivar.all_variables(false)]
end
root ? [self, vars] : vars
end
end
def string_variables(vars, lb="\n", indent="\t", current_indent="")
out = "#{vars[0].inspect}#{lb}"
current_indent += indent
out += vars[1].map do |var, ivar|
ivstr = string_variables(ivar, lb, indent, current_indent)
"#{current_indent}#{var}: #{ivstr}"
end.join
return out
end
def inspect_variables(obj, lb="\n", indent="\t", current_indent="")
string_variables(obj.all_variables, lb, indent, current_indent)
end
该Object#all_variables
方法生成一个数组,其中包含 (0) 给定对象和 (1) 哈希映射实例变量名称到包含 (0) 实例变量和 (1) 哈希映射的数组……。因此,它为您提供了一个很好的递归结构。该string_variables
函数很好地打印出该哈希值;inspect_variables
只是一个方便的包装。因此,print inspect_variables(foo)
为您提供了一个换行符分隔的选项,并print inspect_variables(foo, "<br />\n")
为您提供了带有 HTML 换行符的版本。如果你想指定缩进,你也可以这样做:print inspect_variables(foo, "\n", "|---")
产生一个(无用的)人造树格式而不是基于制表符的缩进。
应该有一种明智的方法来编写一个each_variable
您提供回调的函数(不必分配中间存储);如果我想到了什么,我会编辑这个答案以包含它。 编辑1:我想到了一些东西。
这是另一种写法,我认为它稍微好一点:
class Object
def each_variable(name=nil, depth=0, parent=nil, &block)
yield name, self, depth, parent
self.instance_variables.each do |var|
self.instance_variable_get(var).each_variable(var, depth+1, self, &block)
end
end
end
def inspect_variables(obj, nl="\n", indent="\t", sep=': ')
out = ''
obj.each_variable do |name, var, depth, _parent|
out += [indent*depth, name, name ? sep : '', var.inspect, nl].join
end
return out
end
该Object#each_variable
方法采用许多可选参数,这些参数并非旨在由用户指定;相反,它们被递归用来维护状态。给定块被传递 (a) 实例变量的名称,或者nil
如果该变量是递归的根;(b) 变量;(c) 递归下降的深度;(d),当前变量的父级,或者nil
如果所述变量是递归的根。递归是深度优先的。该inspect_variables
函数使用它来构建一个字符串。obj
参数是要迭代的对象;nl
是行分隔符;indent
是要在每个级别应用的缩进;并将sep
名称和值分开。
编辑 2:这并没有真正为您的问题的答案添加任何内容,但是:只是为了证明我们在重新实现中没有丢失任何东西,这里是all_variables
根据each_variables
.
def all_variables(obj)
cur_depth = 0
root = [obj, {}]
tree = root
parents = []
prev = root
obj.each_variable do |name, var, depth, _parent|
next unless name
case depth <=> cur_depth
when -1 # We've gone back up
tree = parents.pop(cur_depth - depth)[0]
when +1 # We've gone down
parents << tree
tree = prev
else # We're at the same level
# Do nothing
end
cur_depth = depth
prev = tree[1][name] = [var, {}]
end
return root
end
我觉得它应该更短,但这可能是不可能的;因为我们现在没有递归,所以我们必须显式地维护堆栈(in parents
)。但这是可能的,所以该each_variable
方法同样有效(我认为它更好一点)。