Ruby splat运算符在循环内改变值

时间:2014-10-17 12:44:54

标签: ruby splat

我想定义一个方法,它可以采用可选数量的参数和哈希值,如此

def foo(*b, **c)
  2.times.map.with_index { |i|
    new_hash, new_array = {}, b
    c.map { |key, value| new_hash[key] = value[i] unless value[i].nil? }
    new_array << new_hash if new_hash.length > 0
    send(:bar, new_array)
  }
end

def bar(*b)
  p b
end

如果我已正确理解splat和double splat运算符(我怀疑),那么这应该将数组b发送到bar方法,并且只添加new_hash来自foo,如果它包含某些内容。然而,发生了一些奇怪的事情 - 我将尝试用下面的一些片段进行说明

# invoking #foo 
foo(a, key: 'value')

# first iteration of loop in #foo
  # i is 0
  # b is []
  # c is { :key => ['value1'] }
  # send(:bar, new_array) => send(:bar, [{:key => 'value1'}])
  # bar yields: [{:key => 'value1'}]

然而,现在发生了一些事情

# second iteration of loop in #foo
  # i is 1
  # b is [:key => 'value1'] <---- why?
  # c is { :key => ['value1']

为什么在b的循环中foo的值发生了变化?

编辑更新代码以反映为每次迭代创建的新数组

1 个答案:

答案 0 :(得分:2)

new_hash, new_array = {}, b

这不会创建b的副本。现在new_arrayb指向同一个对象。在原地修改一个将修改另一个。

new_array << new_hash

它会修改new_array(以及b),因此新元素将保留在下一次迭代中。使用类似+的内容创建副本:

send(:bar, *(b + (new_hash.empty? ? [] : [new_hash])))