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

时间:2019-03-25 06:14:29

标签: arrays ruby mutable

我有一个对象数组:

$("#hide").addClass('dblock').removeClass('dnone')

我从原始数组创建了一个克隆来执行映射功能,然后映射到每个元素上以使其电子邮件属性变为大写:

class Person
  attr_accessor :email

  def initialize(email)
    @email = email
  end
end

array = [
  Person.new('hello@gmail.com'),
  Person.new('world@gmail.com')
]

它会改变原始数组。我已经尝试过clone = array.clone clone.map { |obj| obj.email.upcase! obj } puts array.inspect # why is the original being mutated puts clone.inspect dup。和我得到相同的结果。在对每个元素的属性进行操作时,clone为什么会突变对象?

1 个答案:

答案 0 :(得分:3)

您克隆了包含Person引用的 array ,但没有更改该数组;您自己更改了Person实例。 clone是所谓的“浅克隆”,它仅复制接收者对象,而不复制其引用可能包含的对象。

在现实世界的逻辑中:您拿了一张纸,上面写着“珍妮,蒂米”。然后将其复制到另一张纸上。然后,您拿了第一张纸,找到了它所指的人,然后给了他们一个苹果。然后,您拿了第二张纸,找到了上面的人,想知道他们的苹果来自哪里。但是只有一个蒂米,只有一个珍妮:您给第一个清单的珍妮一个苹果,第二个清单的珍妮也有一个苹果。

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

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

(请注意,我没有使用clone.email.upcase!。原因再次是相同的原因:如果克隆对象,则它们都将为email使用相同的字符串。{{1 }}更改该字符串,这会使克隆的电子邮件和原始电子邮件均大写。因此,我们为克隆创建了新的电子邮件字符串。)

通过使用this tool逐步进行可视化,可以最好地理解这种情况。但是,该工具运行的是Ruby 2.2,它对upcase!不了解。该代码是等效的:

yield_self

您也可以编写此代码,尽管它无法清晰显示:

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