From: Colin Bartlett Date: 2010-08-24T20:00:23+09:00 Subject: Re: assert "foo"[3] != "foo"[3,1]...revisited --0016367b6c30d77257048e8fabd8 Content-Type: text/plain; charset=windows-1252 Content-Transfer-Encoding: quoted-printable On Tue, Aug 24, 2010 at 2:55 AM, timr wrote: >On Aug 23, 5:40 am, Yukihiro Matsumoto wrote: >> on 23 Aug 2010 20:10:23 +0900, Colin Bartlett wrote: >>> I think it's worth adding something like Matz's wording to >>> http://www.ruby-doc.org/core/classes/String.html#M000771 >>> http://www.ruby-doc.org/core/classes/Array.html#M002160 >>> because reasons for why things work a particular way do help people >>> (well, at least me) remember behaviour. >> >> Yep, description proposal is welcome. >> matz. > > ... And here is a proposal for the updated change to the documentation: > Of course, simplicity/elegance is in the mind of the beholder. But, > I hope that you might consider the option of eliminating the divergent > behavior of String#[] and String[,] when the index =3D=3D str.length. At the end of this post I've put examples of what I had in mind for slice methods which behave that way. The following is a try at modifying the documentation for Array. (I'm using Array because that doesn't have the complication of the change in behaviour of string[index] from 1.8 to 1.9, and because the current documentation for Array does have the special cases, albeit I think it could perhaps be more precise. Adaptation to String should be straightforward.) As much as possible the try uses the existing documentation with minimal changes, and I've included Matz's explanation with what I hope are appropriate changes for array. Comments (not intended to be included in the documentation) are /* comment */. (Apologies in advance if the formatting is weird: Gmail sometimes deletes leading spaces (and others?) when it thinks it knows better than me.) array[index] --> obj or nil array[start, length] --> an_array or nil array[range] --> an_array or nil array.slice(index) --> obj or nil array.slice(start, length) --> an_array or nil array.slice(range) --> an_array or nil Element Reference=97Returns the element at index, or returns a subarray starting at start and continuing for length elements, or returns a subarray specified by range. Negative indices count backward from the end of the array (-1 is the last element). /* start a new line to highlight that an out of range index does not always return nil */ Returns nil if the start (or starting index) are out of range: /* suggested additional documentation */ /*new line*/ *unless* there is a length *and* Integer(start) =3D=3D lengt= h; /*new line*/ *or* the argument is a range *and* Integer(range.begin) =3D= =3D length. /*new line*/ For these special cases (see the table of examples) the return value is an empty array []. /*new line*/ The reason for this special behaviour is that when the start is within the array the sought length is rounded /* or use "truncated"? */ to fit in the size. In the special cases examples 5 (which is the length of the array) is considered as touching the end of the array, so the returned value is a subarray with length zero. /*back to current documentation */ a =3D [ "a", "b", "c", "d", "e" ] # a.length =3D=3D 5 a[2] + a[0] + a[1] #=3D> "cab" a[6] #=3D> nil a[1, 2] #=3D> [ "b", "c" ] a[1..3] #=3D> [ "b", "c", "d" ] a[4..7] #=3D> [ "e" ] a[6..10] #=3D> nil a[-3, 3] #=3D> [ "c", "d", "e" ] /* suggested additional documentation */ The following table shows the special cases behaviour when the start position is just past the end of the array. index/ start a[index] a[start, 2] a[start..7] ----- -------- ------------ ------------ 3 "d" [ "d", "e" ] [ "d", "e" ] 4 "e" [ "e" ] [ "e" ] 5 nil [] [] # <-- special cases 6 nil nil nil *** *** example additional slice methods which return nil *** *** if the start position is outside the array, even if *** *** the start position is only just after the end of the array module Array_String_at_slice # Intended for Array and String: behave like #[], #slice and #slice! # except when the arguments are not just an index, # that is the arguments are a range or an index _and_ a length, # *and* Integer(range.begin) or Integer(index) =3D=3D array_string.length= , # when #[], #slice and #slice! return an empty array [] or string "", # but #at_slice and #at_slice! return nil. # In other words, if #at(index) or #at(range.begin) would return nil # then #at_slice(index, arg), #at_slice!(index, arg), # #at_slice(range) and #at_slice!(range) also return nil. # The method names are intended to convey that the slice behaviour # is similar to #at. Using the name #slice_at was considered, # but rejected (wrongly?) as possibly being capable of being assumed # to be a synonym for #slice. def at_slice( *args ) unless Numeric =3D=3D=3D (ii =3D args[0]) then ii =3D ii.begin end if ii >=3D self.size then nil else slice( *args ) end end def at_slice!( *args ) unless Numeric =3D=3D=3D (ii =3D args[0]) then ii =3D ii.begin end if ii >=3D self.size then nil else slice!( *args ) end end end class Array include Array_String_at_slice end --0016367b6c30d77257048e8fabd8--