Reputation: 1004
There are two methods below; both are the same except one clone
s the input whereas the other does not.
Method 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]
Method 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]
Without the clone
, the method will mutate the original input even if I perform all operations on a copy of the original array.
Why is an explicit clone
method needed to avoid this mutation?
Upvotes: 3
Views: 420
Reputation: 114178
[...] will mutate the original input even if I perform all operations on a copy of the original array.
You don't perform the operations on a copy. When doing
new = array
it doesn't result in a copy operation. Instead, the assignment makes new
simply refer to the same object array
is referring to. It therefore doesn't matter if you invoke new.reject!
or array.reject!
because reject!
is sent to the same receiver.
Why is an explicit
.clone
method needed to avoid this mutation?
Because clone
performs the copy operation you've assumed for =
. From the docs:
Produces a shallow copy of obj [...]
Another way to avoid this mutation is to use a non-mutating method instead:
def remove_smallest(array)
array.reject {|i| i <= array.min }
end
or – to avoid re-calculating the minimum on each step:
def remove_smallest(array)
min = array.min
array.reject {|i| i <= min }
end
You can also use ==
instead of <=
because min
is already the smallest possible value.
Alternatively, there's Array#-
:
def remove_smallest(array)
array - [array.min]
end
Upvotes: 10