Achei a explicação de Gary Wright muito útil também.
http://www.ruby-forum.com/topic/1393096#990065
A resposta de Gary Wright é -
http://www.ruby-doc.org/core/classes/Array.html
Os documentos certamente poderiam ser mais claros, mas o comportamento real é autoconsistente e útil. Nota: Estou assumindo a versão 1.9.X do String.
Ajuda a considerar a numeração da seguinte maneira:
-4 -3 -2 -1 <-- numbering for single argument indexing
0 1 2 3
+---+---+---+---+
| a | b | c | d |
+---+---+---+---+
0 1 2 3 4 <-- numbering for two argument indexing or start of range
-4 -3 -2 -1
O erro comum (e compreensível) também é assumir que a semântica do índice de argumento único é igual à semântica do
primeiro argumento no cenário de dois argumentos (ou intervalo). Eles não são a mesma coisa na prática e a documentação não reflete isso. O erro, porém, está definitivamente na documentação e não na implementação:
argumento único: o índice representa uma posição de caractere único na cadeia. O resultado é a sequência de caracteres únicos encontrada no índice ou nula porque não há caracteres no índice fornecido.
s = ""
s[0] # nil because no character at that position
s = "abcd"
s[0] # "a"
s[-4] # "a"
s[-5] # nil, no characters before the first one
dois argumentos inteiros: os argumentos identificam uma parte da sequência a ser extraída ou substituída. Em particular, partes da cadeia com largura zero também podem ser identificadas para que o texto possa ser inserido antes ou depois dos caracteres existentes, inclusive na frente ou no final da cadeia. Nesse caso, o primeiro argumento não identifica uma posição de caractere, mas identifica o espaço entre os caracteres, conforme mostrado no diagrama acima. O segundo argumento é o comprimento, que pode ser 0.
s = "abcd" # each example below assumes s is reset to "abcd"
To insert text before 'a': s[0,0] = "X" # "Xabcd"
To insert text after 'd': s[4,0] = "Z" # "abcdZ"
To replace first two characters: s[0,2] = "AB" # "ABcd"
To replace last two characters: s[-2,2] = "CD" # "abCD"
To replace middle two characters: s[1..3] = "XX" # "aXXd"
O comportamento de um intervalo é bastante interessante. O ponto inicial é o mesmo que o primeiro argumento quando dois argumentos são fornecidos (como descrito acima), mas o ponto final do intervalo pode ser a 'posição do caractere' como na indexação única ou a "posição da aresta" como com dois argumentos inteiros. A diferença é determinada pelo uso do intervalo de pontos duplos ou triplo:
s = "abcd"
s[1..1] # "b"
s[1..1] = "X" # "aXcd"
s[1...1] # ""
s[1...1] = "X" # "aXbcd", the range specifies a zero-width portion of
the string
s[1..3] # "bcd"
s[1..3] = "X" # "aX", positions 1, 2, and 3 are replaced.
s[1...3] # "bc"
s[1...3] = "X" # "aXd", positions 1, 2, but not quite 3 are replaced.
Se você voltar a esses exemplos e insistir e usar a semântica de índice único para os exemplos de indexação dupla ou de intervalo, ficará confuso. Você precisa usar a numeração alternativa mostrada no diagrama ascii para modelar o comportamento real.