使用默认值混合关键字参数和参数会重复哈希?

时间:2018-02-02 13:32:33

标签: ruby hash parameter-passing keyword-argument

所以我发现了这种红宝石行为,让我疯狂了一个多小时。当我将哈希传递给具有哈希值和关键字参数的默认值的函数时,似乎引用没有被正确传递。只要我删除默认值OR关键字参数,该函数就会按预期运行。我在这里错过了一些明显的红宝石规则吗?

def change_hash(h={}, rand: om)
  h['hey'] = true
end

k = {}
change_hash(k)
k
#=> {}

一旦取出默认值或关键字arg,它就可以正常工作。

def change_hash(h, rand: om)
  h['hey'] = true
end

k = {}
change_hash(k)
k
#=> {'hey' => true}
def change_hash(h={})
  h['hey'] = true
end

k = {}
change_hash(k)
k
#=> {'hey' => true}

修改

感谢您的回答。大多数人都指出ruby在某些情况下会将哈希解析为关键字参数。但是,我在谈论哈希有字符串键的情况。当我传递哈希时,似乎传递的值是正确的。但是修改函数内的哈希并不会修改原始哈希。

def change_hash(hash={}, another_arg: 300)
  puts "another_arg: #{another_arg}"
  puts "hash: #{hash}"
  hash['hey'] = 3
end

my_hash = {"o" => 3}
change_hash(my_hash)
puts my_hash 

打印

another_arg: 300
hash: {"o"=>3}
{"o"=>3}

2 个答案:

答案 0 :(得分:3)

TL; DR ruby​​允许传递散列作为关键字参数以及“扩展的inplace散列”。由于change_hash(rand: :om)必须路由到关键字参数,因此change_hash({rand: :om})也应如此因此,change_hash({})

由于ruby允许在任何位置使用默认参数,因此解析器首先处理默认参数。这意味着,默认参数是贪婪的,并且大多数默认值将占据一席之地。

另一方面,由于ruby缺少函数子句的模式匹配功能,因此解析给定参数以决定是否应将其作为double-splat传递会导致巨大的性能损失。由于使用显式关键字参数(change_hash(rand: :om))的调用应该明确地将:om传递给关键字参数,而允许我们将显式哈希{rand: :om} 传递为关键字参数,Ruby无需接受任何哈希作为关键字参数。

Ruby将在hashrand之间拆分单个哈希参数:

k = {"a" => 42, rand: 42}
def change_hash(h={}, rand: :om)
  h[:foo] = 42
  puts h.inspect
end
change_hash(k);
puts k.inspect

#⇒ {"a"=>42, :foo=>42}
#⇒ {"a"=>42, :rand=>42}

该分割功能要求在传递之前将参数克隆。这就是原始哈希没有被修改的原因。

答案 1 :(得分:0)

这确实是Ruby中特别棘手的情况。

在您的示例中,您有可选参数这是一个哈希,并且您同时拥有可选关键字参数。在这种情况下,如果只传递一个哈希值,Ruby会将其解释为包含关键字参数的哈希值。以下是澄清的代码:

change_hash({rand1: 'om'})
# ArgumentError: unknown keyword: rand1

要解决此问题,您可以将两个单独的哈希值传递给方法,第二个哈希值(关键字参数的哈希值)为空:

def change_hash(h={}, rand: 'om')
  h['hey'] = true
end

k = {}
change_hash(k, {})
k
#=> {'hey' => true}

从实际的角度来看,最好避免像生产代码中那样使用metdhod签名,因为在使用该方法时很容易出错。