Почему ruby ​​изменяет мой массив, когда я его не спрашиваю? - PullRequest
3 голосов
/ 20 марта 2019

Есть два метода ниже;оба одинаковы, за исключением одного clone s ввода, тогда как другого нет.

Метод 1

arr = [1,2,3,1,2,3]

def remove_smallest(array)
  new = array
  new.reject! {|i| i <= new.min}
  new
end

remove_smallest(arr)
#=> [2,3,2,3]
arr
#=> [2,3,2,3]

Метод 2

arr = [1,2,3,1,2,3]

def remove_smallest(array)
  new = array.clone
  new.reject! {|i| i <= new.min}
  new
end

remove_smallest(arr)
#=> [2,3,2,3]
arr
#=> [1,2,3,1,2,3]

Без clone метод изменяет исходный ввод, даже если я выполняю все операции с копией исходного массива.

Зачем нужен явный метод cloneчтобы избежать этой мутации?

1 Ответ

10 голосов
/ 20 марта 2019

[...] изменяет исходный ввод, даже если я выполняю все операции с копией исходного массива.

Вы не выполняете операции с копией.При выполнении

new = array

это не приводит к операции копирования.Вместо этого присваивание заставляет new просто ссылаться на тот же объект, на который ссылается array.Следовательно, не имеет значения, вызываете ли вы new.reject! или array.reject!, потому что reject! отправляется тому же получателю.

Почему нужен явный метод .clone, чтобы избежать этой мутации?

Поскольку clone выполняет операцию копирования, которую вы предполагали для =.Из документов:

Создает поверхностную копию obj [...]

Еще один способ избежать этой мутации - использовать невместо этого используется метод мутации:

def remove_smallest(array)
  array.reject {|i| i <= array.min }
end

или - чтобы избежать перерасчета минимума на каждом шаге:

def remove_smallest(array)
  min = array.min
  array.reject {|i| i <= min }
end

Вы также можете использовать == вместо <=, потому что min - это уже наименьшее возможное значение.

В качестве альтернативы есть Array#-:

def remove_smallest(array)
  array - [array.min]
end
...