Why Array#slice and Array#slice! behave differently?

3.6k views Asked by At

I could not understand why, in Ruby, Array#slice and Array#slice! behave differently than Array#sort and Array#sort! (in the way that one returns the results on a new Array and the other works on the current object).

With sort the first one (without the bang), returns a sorted copy of the current Array, and sort! sorts the current Array.

slice, returns an Array with the specified range, and slice! deletes the specified range from the current object.

What's the reason the Array#slice! behaves like this instead of making the current object an Array with the specified range?

Example:

a = [0,1,2,3,4,5,6,7,8,9]

b = a.slice( 2,2 )

puts "slice:"
puts "  a = " + a.inspect
puts "  b = " + b.inspect

b = a.slice!(2,2)
puts "slice!:"
puts "  a = " + a.inspect
puts "  b = " + b.inspect

Output:

slice:
  a = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  b = [2, 3]
slice!:
  a = [0, 1, 4, 5, 6, 7, 8, 9]
  b = [2, 3]

http://ideone.com/77xFva

3

There are 3 answers

4
wacko On BEST ANSWER

#slice and #slice! behaviors are equivalent: both "return a subarray starting at the start index and continuing for length elements", the same way as #sort and #sort! return a sorted array or #reverse and #reverse! return a reversed array.

The difference is that the bang methods also modify the object itself.

a = [4,2,6,9,1,5,8]
b = a.dup
a.sort == b.sort!             # => true
a == b                        # => false

b = a.dup
a.reverse == b.reverse!       # => true
a == b                        # => false

b = a.dup
a.slice(2,2) == b.slice!(2,2) # => true
a == b                        # => false
0
jcomo On

I think the idea behind the design decision is that if you call Array#slice! then you already have a pointer to the slice of the array (assuming you assigned it to a variable). The logical decision is to modify the object so that the slice is no longer in the original array.

You can see how this would be useful in a loop, for instance. If you wanted to keep taking a slice of the first 3 elements of the array, do something with those 3 elements, and stop when there was no more, you could make use of Array#slice! and it would eventually destroy the array.

Now if you imagine that array from the example was a queue or a stack, it would make sense as to why you want to remove portions of the array.

2
Jed Schneider On

! or bang methods in ruby typically mutate the existing object rather than the non-bang method equivalent that returns a new object. If you want to modify the existing object, use the bang method option.

EDIT: to address the design decision, I'm not Matz, but I am going to guess that because the nature of slice is to return a subset, it returns the subset in each case. For other bang methods like gsub or sort you are modifying (potentially) the entire string/object so it either returns a copy or returns the original with changes.