负数索引在 `Array#[]=` 中是如何工作的?

4

我尝试了解如何使用Array#[]=,并进行了一些操作:

enum[int] = obj → obj
enum[start, length] = obj → obj
enum[range] = obj → obj

问题1

我有一个数组b,它在索引0处保存了nil

b = []
b[0]   # => nil

我试图在以下代码中用整数10替换nil

b[-1] = 10 # => IndexError: index -1 too small for array; minimum: 0

为什么上面的代码不起作用,而下面的代码却可以?在数组大小为1的情况下,为什么索引0和-1被区别对待?
b[0] = 5   # => 5
b[-1] = 10 # => 10

问题2

我创建了一个大小为2的数组,并执行了以下操作:

a = [1,2]

a[-3] = 3       # => IndexError: index -3 too small for array; minimum: -2
a[-3] = [3]     # => IndexError: index -3 too small for array; minimum: -2
a[-3..-4] = [3] # => RangeError: -3..-4 out of range

我相信负索引永远不会增加数组的大小,但是我不知道为什么。为什么下面的代码成功了呢?

a[-2..-3] = [3,4] #=> [3, 4]

好问题,试图找出一个原因。 - Arindam
@Arindam 谢谢你的赞美.. :) - Arup Rakshit
空数组如 b = [] 并不包含 nil。如果给定索引处的值不存在,它只会返回 nil> b[6789] => nil - David Unric
@DavidUnric 嗯,你说得完全正确。 - Arup Rakshit
3个回答

7
我建议您查看Array文档中的第一段。令人惊讶的是,它说:“负索引被认为是相对于数组末尾的位置,也就是说,索引-1表示数组的最后一个元素,索引-2表示数组中倒数第二个元素,以此类推。”
这意味着,只有当|N| <= a.size时,才能设置a[-N]元素。这就是为什么a = [1,2] ; a[-3] = 3失败了(3 > 2)。
另一方面,Ruby数组可能存在未记录的功能:a[INBOUNDS_IDX..NONSENSE_IDX]=SMTH将在INBOUNDS_IDX索引之前插入SMTHNONSENSE_IDX是无意义的。
a=[1,2]
a[2..0]='a'
a[2..1]='b'
a[2..-100]='c'
# ⇒ [1, 2, "c", "b", "a"]
a[2..-1]='q'
# ⇒ [1, 2, "q"]

这里的"Nonsense"是指“小于INBOUNDS_IDX,不能被负数表示为索引”(这就是为什么上面的示例中a [2..-1]被视为a [2..(a.size-1)]的原因)。


我没有找到这个 a[-2..-3] = [3,4] #=> [3, 4] 的答案。你能告诉我在哪里可以找到吗? - Arup Rakshit
@LoveConcept a[-2..-3] = [3,4] #=> [3, 4] 表示 [3,4] 是从 Array#[] 方法返回的值。如果你使用 puts a 打印 a 的值,你会看到预期的 #⇒[3,4,1,2] - Aleksei Matiushkin
如果您尝试 a = [1,2] 然后 a[-3] = 5,会产生错误 IndexError: index -3 too small for array; minimum: -2。但是尝试 a[-2..-4] =15 就可以了。在这里我感到困惑。请帮助我理解这个概念好吗?新的数组看起来像 >> a #=> [15, 1, 2]。在这里,数组也可以通过负索引设置或添加新元素,但为什么第一次尝试不行呢? - Arup Rakshit
你可能看到的唯一一个是我的 :-) - Aleksei Matiushkin
我认为这并没有得到很好的解释。关键在于,如果使用Range进行数组切片,它并不会“扩展”它,而只是使用#first#last方法获取其边界。现在它开始有意义了。 - David Unric
显示剩余3条评论

0

观察 #1

-1 索引与最后一个元素有关,如果数组没有大小,[],你不能使用它,直到你用一个或多个元素初始化它。

观察 #2:

是的,你是对的,负索引永远不会增加数组的大小,它只引用数组中一个具体存在的位置。

不要认为数组是循环的——0 索引指向 N-1 索引——所以你不能使用任何负索引来认为它是有效的。


1
如果是这样,那么你会如何解释 a[-2..-3] = [3,4] - Arup Rakshit

0

问题1:

一个空数组没有元素,所以当你尝试用负索引-1设置其第0个元素时,会出现错误。因为负索引从数组末尾循环。

所以a = []; a[-1] = 3会导致以下两点不可能实现:

a) 获取最后一个位置的元素,因为它是空的

b) 设置其值,因为它从未被捕获。

a[0] = 5可以工作,因为你告诉编译器:

a) 获取第一个元素,

b) 如果不存在,则创建一个,并将其分配给你请求的值。

请参见官方API文档,特别提到正索引可以增加大小,负索引超过数组开头会引发错误。

问题2:

上述解释几乎也回答了第二个问题。

给定a = [1,2]

a[-3] = 3 会导致第一个断点。你试图访问倒数第三个元素,但它不存在。这是设计上的问题,因此会出现错误。

a[-2..-3] 在定义的数组捕获范围内。

你要求解释器捕获倒数第二个元素(在这种情况下是从前面算起的第一个元素),并尝试调用一个范围,该范围要求增加数组的大小,并使用你请求的内容填充它。

幸运的是,一切仍然很好,符合预期。这是个好消息。


1
“并尝试调用一个范围”——这并不完全正确。a[-2..-3]a[-2..0]一样处理,这两个范围通常都是空的。 - Aleksei Matiushkin
@mudasobwa 错了,它们与 a[-2..0] 不同,后者是在 a.size 范围内非空范围 [-2, -1, 0] 的切片数组。 - David Unric
@DavidUnric,能否请您提供一个数组有不止两个元素的实例,在这个实例中,应用 a[-2..-3]=SMTH 的结果与 a[-2..0]=SMTH 的结果不同?如果没有,那么请考虑它们_被视为相同_. 通过“范围为空”,我(虽然不是很清楚)指的是“应用 Array#[] 的结果为空”。 - Aleksei Matiushkin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接