【问题标题】:Mixing keyword argument and arguments with default values duplicates the hash?将关键字参数和具有默认值的参数混合使用会重复哈希?
【发布时间】:2018-02-02 13:32:33
【问题描述】:

所以我发现了这种 ruby​​ 行为,这让我疯狂了一个多小时。当我将哈希传递给具有哈希默认值和关键字参数的函数时,似乎引用没有正确传递。一旦我去掉默认值或关键字参数,函数就会按预期运行。我在这里遗漏了一些明显的红宝石规则吗?

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 会将散列解析为关键字参数。但是,我说的是哈希具有字符串键的情况。当我传递哈希时,传递的值似乎是正确的。但是修改函数内部的hash并不会修改原来的hash。

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}

【问题讨论】:

    标签: ruby hash parameter-passing keyword-argument


    【解决方案1】:

    这在 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签名,因为在使用该方法时很容易出错。

    【讨论】:

      【解决方案2】:

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


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

      另一方面,由于 ruby​​ 缺少函数子句的模式匹配功能,解析给定参数以决定是否应将其作为双拼法传递会导致巨大的性能损失。由于带有显式关键字参数 (change_hash(rand: :om)) 的调用肯定应该将 :om 传递给关键字参数,并且我们可以将显式哈希 {rand: :om} 作为关键字参数传递,因此 Ruby 什么都没有要做,但要接受 any 哈希作为关键字参数。


      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}
      

      该拆分功能需要在传递之前克隆参数。这就是为什么原始哈希没有被修改的原因。

      【讨论】:

      • 但是为什么看起来我在函数中得到了正确的值? repl.it/repls/SafeCanineAndeancat
      • 我不确定我是否遵循。 “获得正确的价值观”是什么意思?在repl 中,您传递了两个参数,没有默认值。
      • 哎呀对不起,错误的repl。更新repl.it/repls/SafeCanineAndeancat
      • 我仍然怀疑我是否跟随。 hash 打印它的默认值,而不是你传入的那个。检查它是__id__
      • 从我坐的地方打印出的散列是我通过的,而不是默认的:/ id 不同,是的,我预计问题出在哪里。看起来哈希是在幕后克隆的。
      猜你喜欢
      • 2017-01-27
      • 2013-02-14
      • 1970-01-01
      • 1970-01-01
      • 2014-01-31
      • 2021-11-01
      • 1970-01-01
      • 2016-09-05
      • 1970-01-01
      相关资源
      最近更新 更多