下面有两种方法:两者都是相同的,除了一个clone是输入,而另一个不是。
方法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]
Run Code Online (Sandbox Code Playgroud)
方法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]
Run Code Online (Sandbox Code Playgroud)
如果不使用clone,即使我对原始数组的副本执行所有操作,该方法也会使原始输入发生变化。
为什么clone需要一种明确的方法来避免这种突变?
Ste*_*fan 10
[...] 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
Run Code Online (Sandbox Code Playgroud)
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
.clonemethod 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
Run Code Online (Sandbox Code Playgroud)
or – to avoid re-calculating the minimum on each step:
def remove_smallest(array)
min = array.min
array.reject {|i| i <= min }
end
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)