我试图通过作为参数传入的键提取哈希值:
def menu
@menu = {"burger" => 5, "chips" => 2, "coke" => 1}
end
def receive_order(*items)
raise "Sorry not in stock" if items.all? { |key|@menu.has_key?(key) } == false
:ordered if items.all? { |key|@menu.has_key?(key) }
# I was hoping to do something like this...
bill << menu.values_at(items)
# bill being an empty array
end
我在IRB中玩过这个,然后返回[nil]
。有人能够解释原因吗?
如果我输入以下内容:
menu.values_at("burger", "chips")
我明白了:
=> [5,2]
那么为什么在作为参数传递时它不会起作用?
答案 0 :(得分:7)
Hash#values_at
将会起作用。
在发布的示例中,values_at
中的方法Hash
将传递的数组视为单个键,而不是将数组的各个元素视为单独的键。
尝试:
bill << menu.values_at(*items)
REPL中的示例(irb
/ pry
):
>> hash = {a: 12, b: 7, c: 23}
=> {:a=>12, :b=>7, :c=>23}
>> keys_to_search = [:a, :b]
=> [:a, :b]
>> hash.values_at keys_to_search
=> [nil]
>> hash.values_at *keys_to_search
=> [12, 7]
答案 1 :(得分:3)
values_at
中的splat之外,还有一些问题,以及一些改进的机会。
首先:
def menu
@menu = {"burger" => 5, "chips" => 2, "coke" => 1}
end
在调用menu
并将实例变量@menu
设置为等于该哈希值时,返回哈希值都没有意义。我建议你用以下任何一个代替:
@menu = {"burger" => 5, "chips" => 2, "coke" => 1}
或
def menu
{"burger" => 5, "chips" => 2, "coke" => 1}
end
并分别通过引用receive_order
或@menu
从menu
方法获取哈希值。
接下来,如果商品没有库存,您为什么要提出异常?对于必须是常见情况而言,这似乎相当激烈。我希望你能以更优雅的方式处理它。另一方面,也许item
的每个元素都应该是哈希中的一个键,并且该消息具有误导性。我们来看看那行代码:
raise "Sorry not in stock" if items.all? { |key| @menu.has_key?(key) } == false
这有点令人困惑。最好写:
raise "Sorry not in stock" unless items.all? { |key| @menu.has_key?(key) }
但更好的是:
raise "Sorry not in stock" if (items - @menu.keys).any?
@menu["chips"] = 0
不会表示“芯片”没有库存吗?如果是这样,并且假设当item
的元素不是哈希中的键时引发异常,则将上述内容更改为:
raise "No keys in @menu for elements @{items - @menu.keys} of items" \
if (items - @menu.keys).any?
if @menu.values_at(*items).any?(&:zero?)
puts "Sorry not in stock"
<do something else?>
else
...
end
如评论中所述,在:
:ordered if items.all? { |key|@menu.has_key?(key) }
您需要使用实际执行某些操作的代码替换:ordered
。但是你需要if
条款吗?如果第一行没有引发异常,if
子句必须评估true
,所以“否”,您不需要if
子句。
接下来,在执行以下操作时(在添加缺失的splat之后)会引发异常:
bill << menu.values_at(*items)
# NameError: undefined local variable or method `bill' for main:Object
因为bill
尚未初始化。我想你只想:
bill = menu.values_at(*items)
但是你不会对bill
做任何事情,除了从方法中返回它的值(因为它是在方法返回之前计算的最后一个表达式的值)。因此,您可以用以下内容替换它:
menu.values_at(*items)
把它们放在一起,我们可能会有这样的事情:
@menu = {"burger" => 5, "chips" => 2, "coke" => 1}
def receive_order(*items)
raise "No keys in @menu for elements @{items - @menu.keys} of items" \
if (items - @menu.keys).any?
if @menu.values_at(*items).any?(&:zero?)
puts "Sorry not in stock"
<do something else?>
else
<perform some action when everything is in stock>
end
@menu.values_at(*items)
end