红宝石地图!不会更改包含的变量

时间:2018-04-19 08:59:08

标签: ruby

假设我有一个对象x和一个包含y=[x]的数组x。如果我操纵x,则y不会改变:

x = 1 # => 1
y = [x] # => [1]
x = x+1 # => 2
x # => 2
y # => [1]

如果我更改y,则x不会更改。

x = 1 # => 1
y = [x] # => [1]
y.map!{|a| a+1} # => [2]
y # => [2]
x # => 1

有没有办法让它们并行改变?感觉就像我在数组上map!时,底层值应该改变。

2 个答案:

答案 0 :(得分:3)

首先

x = x + 1

将使用旧名称x

创建一个新变量
x = 1
y = [x]
x.object_id
# 3
y[0].object_id
# 3
x = x + 1
x.object_id
# 5
y[0].object_id
# 3

其次,数字是Ruby中的不可变对象(例如,字符串是可变的)。所以你不能使用Ruby中的数字对象做你想做的事。你能做的就是更加模糊。您可以创建自己的可变对象(数字的容器)并在数组中使用对该对象的引用。

class MutableNumber
  attr_accessor :n

  def initialize(n)
    @n = n
  end
end

x = MutableNumber.new(1)
y = [x]
y[0].n
#=> 1
x.n += 1
y[0].n
#=> 2

你可以更进一步,在这里添加更多魔法来模仿数字

class MutableNumber
  attr_accessor :n

  def initialize(n)
    @n = n
  end

  def method_missing(m, *args, &blk)
    @n = @n.send(m, *args, &blk)
    self
  end
end

x = MutableNumber.new(1)
y = [x]
y[0].n
#=> 1
x += 1
y[0].n
#=> 2

但我不鼓励你这样做。

坚持认为数字是不可变的。总的来说,你应该小心可变性。

答案 1 :(得分:0)

map!对于数组是可变的,而不是对于它的元素。这意味着数组在适当的位置获得新值(即它被分配给原始数组)。数组中的元素不会发生变异,而是被新元素替换。

如果要更改旧值,可以使用each进行迭代,并在每个元素上调用mutating方法。您可以通过字符串数组看到这一点:

a = "a"; b = "b"; aa = [a, b]
#=> ["a", "b"]
aa.map!{|e| e.capitalize }
#=> ["A", "B"]
[a, b]
#=> ["a", "b"]

a = "a"; b = "b"; aa = [a, b]
#=> ["a", "b"]
aa.each{|e| e.capitalize! }
#=> ["A", "B"]
[a, b]
#=> ["A", "B"]

请注意,它不适用于不可变对象,数字是不可变的,正如@ fl00r在他的回答中所解释的那样。