我正在参加Ruby Koans的练习,我发现以下我发现真的无法解释的Ruby怪癖:
array = [:peanut, :butter, :and, :jelly]
array[0] #=> :peanut #OK!
array[0,1] #=> [:peanut] #OK!
array[0,2] #=> [:peanut, :butter] #OK!
array[0,0] #=> [] #OK!
array[2] #=> :and #OK!
array[2,2] #=> [:and, :jelly] #OK!
array[2,20] #=> [:and, :jelly] #OK!
array[4] #=> nil #OK!
array[4,0] #=> [] #HUH?? Why's that?
array[4,100] #=> [] #Still HUH, but consistent with previous one
array[5] #=> nil #consistent with array[4] #=> nil
array[5,0] #=> nil #WOW. Now I don't understand anything anymore...
Run Code Online (Sandbox Code Playgroud)
那么为什么array[5,0]不等于array[4,0]?是否有任何理由为什么当你开始在(长+ 1)阵列切片表现这种怪异的个位置?
Ama*_*dan 179
切片和索引是两种不同的操作,并且推断一方的行为是您的问题所在.
slice中的第一个参数不是元素,而是元素之间的位置,定义跨度(而不是元素本身):
:peanut :butter :and :jelly
0 1 2 3 4
Run Code Online (Sandbox Code Playgroud)
4仍在阵列内,只是勉强; 如果你请求0个元素,你得到数组的空端.但是没有索引5,所以你不能从那里切片.
当你做索引(比如array[4])时,你指的是元素本身,所以索引只从0到3.
Jed*_*der 27
这与slice返回一个数组这一事实有关,这是来自Array#slice的相关源文档:
* call-seq:
* 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
Run Code Online (Sandbox Code Playgroud)
这告诉我,如果你给出一个超出范围的开头,它将返回nil,因此在你的例子中array[4,0]要求存在的第四个元素,但要求返回一个零元素的数组.虽然array[5,0]要求索引超出范围,所以它返回nil.如果你记得slice方法返回一个新数组,而不是改变原始数据结构,这可能更有意义.
编辑:
在审核了评论之后,我决定编辑这个答案.当arg值为2时,Slice调用以下代码片段:
if (argc == 2) {
if (SYMBOL_P(argv[0])) {
rb_raise(rb_eTypeError, "Symbol as array index");
}
beg = NUM2LONG(argv[0]);
len = NUM2LONG(argv[1]);
if (beg < 0) {
beg += RARRAY(ary)->len;
}
return rb_ary_subseq(ary, beg, len);
}
Run Code Online (Sandbox Code Playgroud)
如果你查看定义方法的array.c类rb_ary_subseq,你会看到如果长度超出范围,它返回nil,而不是索引:
if (beg > RARRAY_LEN(ary)) return Qnil;
Run Code Online (Sandbox Code Playgroud)
在这种情况下,这是传入4时发生的情况,它检查有4个元素,因此不会触发nil返回.然后,如果第二个arg设置为零,它将继续并返回一个空数组.如果传入5,则数组中没有5个元素,因此在计算零arg之前返回nil.这里的代码是944行.
我认为这是一个错误,或者至少是不可预测的,而不是"最少的惊喜原则".当我得到几分钟时,我将至少向ruby核心提交一个失败的测试补丁.
Mat*_*chu 23
至少要注意行为是一致的.从5开始,一切都是一样的; 怪异只发生在[4,N].
也许这种模式有所帮助,或者我可能只是累了而且根本没有帮助.
array[0,4] => [:peanut, :butter, :and, :jelly]
array[1,3] => [:butter, :and, :jelly]
array[2,2] => [:and, :jelly]
array[3,1] => [:jelly]
array[4,0] => []
Run Code Online (Sandbox Code Playgroud)
在[4,0],我们捕获数组的结尾.我实际上觉得它很奇怪,就模式中的美感而言,如果最后一个返回nil.由于这样的上下文,4第一个参数是可接受的选项,因此可以返回空数组.但是,一旦我们达到5级以上,该方法很可能会立即完全脱离界限.
Fra*_*rba 12
当您考虑时,这是有意义的,而数组切片可以是有效的左值,而不仅仅是右值:
array = [:peanut, :butter, :and, :jelly]
# replace 0 elements starting at index 5 (insert at end or array):
array[4,0] = [:sandwich]
# replace 0 elements starting at index 0 (insert at head of array):
array[0,0] = [:make, :me, :a]
# array is [:make, :me, :a, :peanut, :butter, :and, :jelly, :sandwich]
# this is just like replacing existing elements:
array[3, 4] = [:grilled, :cheese]
# array is [:make, :me, :a, :grilled, :cheese, :sandwich]
Run Code Online (Sandbox Code Playgroud)
如果array[4,0]返回nil而不是,那将无法实现[].但是,array[5,0]返回nil因为它超出了界限(在4元素数组的第4个元素之后插入是有意义的,但是在4个元素数组的第5个元素之后插入不是).
将切片语法读array[x,y]作"从x元素开始array,选择y元素".只有array至少有x元素才有意义.
Dig*_*oss 11
您需要能够分配给这些切片,因此它们的定义方式使得字符串的开头和结尾具有有效的零长度表达式.
array[4, 0] = :sandwich
array[0, 0] = :crunchy
=> [:crunchy, :peanut, :butter, :and, :jelly, :sandwich]
Run Code Online (Sandbox Code Playgroud)
我同意这似乎是一种奇怪的行为,但即使是官方文档也Array#slice证明了与您的示例相同的行为,在下面的"特殊情况"中:
a = [ "a", "b", "c", "d", "e" ]
a[2] + a[0] + a[1] #=> "cab"
a[6] #=> nil
a[1, 2] #=> [ "b", "c" ]
a[1..3] #=> [ "b", "c", "d" ]
a[4..7] #=> [ "e" ]
a[6..10] #=> nil
a[-3, 3] #=> [ "c", "d", "e" ]
# special cases
a[5] #=> nil
a[5, 1] #=> []
a[5..10] #=> []
Run Code Online (Sandbox Code Playgroud)
不幸的是,即使他们的描述Array#slice似乎没有提供任何关于它为什么这样工作的见解:
元素引用 - 返回索引处的元素,或返回从start开始 并继续为length 元素的子数组,或返回由range指定的子数组.负索引从数组末尾向后计数(-1是最后一个元素).如果索引(或起始索引)超出范围,则返回nil.
我发现Gary Wright的解释也很有帮助. http://www.ruby-forum.com/topic/1393096#990065
Gary Wright的回答是 -
http://www.ruby-doc.org/core/classes/Array.html
文档当然可以更清楚,但实际行为是自我一致和有用的.注意:我假设1.9.X版本的String.
它有助于以下列方式考虑编号:
-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
Run Code Online (Sandbox Code Playgroud)
常见(且可理解的)错误也是假设单个参数索引的语义与两个参数场景(或范围)中第一个参数的语义相同 .它们在实践中并不相同,文档也没有反映出这一点.虽然错误肯定在文档中,而不是在实现中:
单个参数:索引表示字符串中的单个字符位置.结果是在索引处找到的单个字符串或nil,因为给定索引处没有字符.
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
Run Code Online (Sandbox Code Playgroud)
两个整数参数:参数标识要提取或替换的字符串的一部分.特别地,还可以识别字符串的零宽度部分,使得可以在包括在字符串的前端或末尾的现有字符之前或之后插入文本.在这种情况下,第一个参数并不能识别一个字符的位置,而是识别为示出在图中上面的字符之间的空间.第二个参数是长度,可以是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"
Run Code Online (Sandbox Code Playgroud)
范围的行为非常有趣.当提供两个参数时,起点与第一个参数相同(如上所述),但是范围的结束点可以是与单个索引一样的"字符位置",也可以是与两个整数参数一样的"边缘位置".差异取决于是使用双点范围还是三点范围:
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.
Run Code Online (Sandbox Code Playgroud)
如果您回顾这些示例并坚持并使用单个索引语义进行双重或范围索引示例,您将会感到困惑.你必须使用我在ascii图中显示的备用编号来模拟实际行为.
Jim Weirich提供的解释
考虑它的一种方法是索引位置4位于数组的最边缘.在请求切片时,您将返回剩余的数组.因此,考虑数组[2,10],数组[3,10]和数组[4,10] ...每个返回数组末尾的剩余位:分别为2个元素,1个元素和0个元素.但是,位置5显然在数组外部而不在边缘,因此数组[5,10]返回nil.
小智 6
考虑以下数组:
>> array=["a","b","c"]
=> ["a", "b", "c"]
Run Code Online (Sandbox Code Playgroud)
您可以通过将项目分配给数组的开始(头部)来插入项目a[0,0].为了把之间的元素"a"和"b",使用a[1,0].基本上,在表示法中a[i,n],i表示索引和n许多元素.何时n=0,它定义了数组元素之间的位置.
现在,如果您考虑数组的结尾,如何使用上述符号将项目追加到最后?简单,将值赋值给a[3,0].这是数组的尾部.
因此,如果您尝试访问该元素a[3,0],您将获得[].在这种情况下,您仍然在数组的范围内.但是如果你试图访问a[4,0],你将得到nil返回值,因为你不再在数组的范围内.
在http://mybrainstormings.wordpress.com/2012/09/10/arrays-in-ruby/上阅读更多相关信息.
| 归档时间: |
|
| 查看次数: |
23498 次 |
| 最近记录: |