为什么map函数在对每个元素的属性进行操作时会改变对象数组?

问题描述 投票:0回答:1

我有一个对象数组:

class Person
  attr_accessor :email

  def initialize(email)
    @email = email
  end
end

array = [
  Person.new('[email protected]'),
  Person.new('[email protected]')
]

我从原始数组创建了一个克隆来执行map函数,然后我映射每个元素以使其email属性变为大写:

clone = array.clone
clone.map { |obj|
  obj.email.upcase!
  obj
}

puts array.inspect # why is the original being mutated
puts clone.inspect

它改变了原始数组。我已经尝试过dupclone。我得到了相同的结果。为什么map在对每个元素的属性进行操作时会改变对象?

arrays ruby mutable
1个回答
3
投票

你克隆了包含Person引用的数组,但你没有更改数组;你自己改变了Person实例。 clone是所谓的“浅层克隆”,它只复制接收器对象,但不包含其可能包含的引用对象。

在现实世界的逻辑中:你拿了一张纸,上面写着“Jenny,Timmy”。然后你把它复制到另一张纸上。然后你拿了第一张纸,找到了它所指的人,并给了他们一个苹果。然后你拿了第二张纸,找到了人们,并想知道他们的苹果来自哪里。但是只有一个Timmy,只有一个Jenny:你给第一个名单的Jenny一个苹果,第二个名单的Jenny也有一个。

如果要克隆某些内容,请克隆Jenny。

array.map { |person|
  person.clone.yield_self { |clone|
    clone.email = clone.email.upcase
  }
}

(请注意,我没有使用clone.email.upcase!。原因是同样的原因:如果你克隆一个对象,他们将使用相同的字符串为emailupcase!更改该字符串,这将大写克隆的电子邮件和原始的因此,我们为克隆创建了一个新的电子邮件字符串。)

通过使用this tool逐步完成可视化,可以最好地理解这样的事情。但是,该工具运行Ruby 2.2,它不知道yield_self;这段代码是等价的:

array.map { |person|
  clone = person.clone
  clone.email = clone.email.upcase
  clone
}

你也可以写这个,虽然它不会清晰地形象化:

array.map(&:clone).map { |clone|
  clone.email = clone.email.upcase
}
© www.soinside.com 2019 - 2024. All rights reserved.