私はおそらく次のように書くでしょう:
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
もっと短くすべきだと思いますが、それは不可能かもしれません。現在は再帰がないため、スタックを明示的に維持する必要があります ( でparents
)。しかし、それは可能であるため、このeach_variable
方法も同様に機能します (そして、少し優れていると思います)。