如何根据提供的参数在 Ruby 中递归定义哈希?
此代码片段填充 @options
哈希值。 values
是一个Array
,其中包含零个或多个异构项。如果您使用 Hash
条目的参数调用 populate
,它将使用您为每个条目指定的值来采用默认值。
def populate(*args)
args.each do |a|
values = nil
if (a.kind_of? Hash)
# Converts {:k => "v"} to `a = :k, values = "v"`
a, values = a.to_a.first
end
@options[:"#{a}"] ||= values ||= {}
end
end
我想要做的是更改populate
,使其递归地填充@options
。有一种特殊情况:如果要填充键的值是一个完全由(1)符号或(2)键为符号(或两者的某种组合)的哈希组成的数组,那么它们应该被视为子键而不是与该键关联的值,并且应该递归地重新应用用于评估原始填充
参数的相同逻辑。
这有点难以用语言表达,所以我编写了一些测试用例。以下是一些测试用例以及之后 @options
的预期值:
populate :a
=> @options is {:a => {}}
populate :a => 42
=> @options is {:a => 42}
populate :a, :b, :c
=> @options is {:a => {}, :b => {}, :c => {}}
populate :a, :b => "apples", :c
=> @options is {:a => {}, :b => "apples", :c => {}}
populate :a => :b
=> @options is {:a => :b}
# Because [:b] is an Array consisting entirely of Symbols or
# Hashes whose keys are Symbols, we assume that :b is a subkey
# of @options[:a], rather than the value for @options[:a].
populate :a => [:b]
=> @options is {:a => {:b => {}}}
populate :a => [:b, :c => :d]
=> @options is {:a => {:b => {}, :c => :d}}
populate :a => [:a, :b, :c]
=> @options is {:a => {:a => {}, :b => {}, :c => {}}}
populate :a => [:a, :b, "c"]
=> @options is {:a => [:a, :b, "c"]}
populate :a => [:one], :b => [:two, :three => "four"]
=> @options is {:a => :one, :b => {:two => {}, :three => "four"}}
populate :a => [:one], :b => [:two => {:four => :five}, :three => "four"]
=> @options is {:a => :one,
:b => {
:two => {
:four => :five
}
},
:three => "four"
}
}
如果需要更改 populate
的签名以适应某种递归版本,这是可以接受的。理论上可能发生的嵌套数量没有限制。
关于我如何实现这一目标有什么想法吗?
This snippet of code populates an @options
hash. values
is an Array
which contains zero or more heterogeneous items. If you invoke populate
with arguments that are Hash
entries, it uses the value you specify for each entry to assume a default value.
def populate(*args)
args.each do |a|
values = nil
if (a.kind_of? Hash)
# Converts {:k => "v"} to `a = :k, values = "v"`
a, values = a.to_a.first
end
@options[:"#{a}"] ||= values ||= {}
end
end
What I'd like to do is change populate
such that it recursively populates @options
. There is a special case: if the values it's about to populate a key with are an Array consisting entirely of (1) Symbols or (2) Hashes whose keys are Symbols (or some combination of the two), then they should be treated as subkeys rather than the values associated with that key, and the same logic used to evaluate the original populate
arguments should be recursively re-applied.
That was a little hard to put into words, so I've written some test cases. Here are some test cases and the expected value of @options
afterwards:
populate :a
=> @options is {:a => {}}
populate :a => 42
=> @options is {:a => 42}
populate :a, :b, :c
=> @options is {:a => {}, :b => {}, :c => {}}
populate :a, :b => "apples", :c
=> @options is {:a => {}, :b => "apples", :c => {}}
populate :a => :b
=> @options is {:a => :b}
# Because [:b] is an Array consisting entirely of Symbols or
# Hashes whose keys are Symbols, we assume that :b is a subkey
# of @options[:a], rather than the value for @options[:a].
populate :a => [:b]
=> @options is {:a => {:b => {}}}
populate :a => [:b, :c => :d]
=> @options is {:a => {:b => {}, :c => :d}}
populate :a => [:a, :b, :c]
=> @options is {:a => {:a => {}, :b => {}, :c => {}}}
populate :a => [:a, :b, "c"]
=> @options is {:a => [:a, :b, "c"]}
populate :a => [:one], :b => [:two, :three => "four"]
=> @options is {:a => :one, :b => {:two => {}, :three => "four"}}
populate :a => [:one], :b => [:two => {:four => :five}, :three => "four"]
=> @options is {:a => :one,
:b => {
:two => {
:four => :five
}
},
:three => "four"
}
}
It is acceptable if the signature of populate
needs to change to accommodate some kind of recursive version. There is no limit to the amount of nesting that could theoretically happen.
Any thoughts on how I might pull this off?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
所以这里有一些有效的简单代码。
它确实偏离了您的测试用例:
populate :a, :b => "apples", :c
是一个 ruby 语法错误。 Ruby 将假定方法的final 参数是一个散列(当未给出大括号时),但不是非最终参数,正如您在此处所假设的那样。给定的代码是语法错误(无论populate
的定义如何),因为它假定:c
是哈希键,并且在查找 < 时找到行尾code>:c 的值。填充:a,{:b => "apples"}, :c
按预期populate :a =>; [:一], :b => [:二,:三=> "four"]
返回{:a=>{:one=>{}}、:b=>{:two=>{}、: Three=>"four" }}
。这与测试用例populate :a => 一致。 [:b]
。So here's some simple code that works.
It does deviate from your test cases:
populate :a, :b => "apples", :c
is a ruby syntax error. Ruby will assume the final argument to a method is a hash (when not given braces), but not a non-final one, as you assume here. The given code is a syntax error (no matter the definition ofpopulate
) since it assumes:c
is a hash key, and finds an end of line when it's looking for:c
's value.populate :a, {:b => "apples"}, :c
works as expectedpopulate :a => [:one], :b => [:two, :three => "four"]
returns{:a=>{:one=>{}}, :b=>{:two=>{}, :three=>"four"}}
. This is consistent with the test casepopulate :a => [:b]
.Ruby 不是 Perl,
=>
只能在真正的哈希定义中工作或作为方法调用中的最终参数。大多数你想要的东西都会导致语法错误。您确定仅限于 Ruby 语法支持的情况的
populate
值得吗?Ruby isn't Perl,
=>
works only inside real Hash definition or as final argument in method call. Most things you want will result in a syntax error.Are you sure that
populate
limited to cases supported by Ruby syntax is worth it?