什么是差一错误(off-by-one error)?如果我有这样的错误,如何修复它?
什么是差一错误(off-by-one error)?如果我有这样的错误,如何修复它?
一个一位偏差错误(off-by-one error)的例子是当你想要执行一个循环n次并写下以下代码时:
for (int i = 1; i < n; ++i) { ... }
或:for (int i = 0; i <= n; ++i) { ... }
在第一种情况下,循环将执行(n-1)
次,在第二种情况下将执行(n+1)
次,这被称为“错位”。其他变化也可能发生,但通常由于循环变量的初始值或循环结束条件中的错误而导致循环执行次数过多或过少。
循环可以正确编写为:
for (int i = 0; i < n; ++i) { ... }
for循环只是while循环的一个特例。在while循环中可能会出现相同类型的错误。
预防这个问题的一种方法是认识到我们的大脑有倾向(也许是认知偏差)犯这个错误。记住这一点可能有助于您识别和预防未来的情况。但我想,防止这个错误的最好方法是编写单元测试。测试将帮助您确保代码正常运行。
for
循环的代码:char exampleArray[] = { 'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd' };
for(int i = 0; i <= 11; i++)
{
print(exampleArray[i])
}
看到问题了吗?因为我数了一下我的数组有11个字符,所以我设置了循环迭代11次。然而,在大多数编程语言中,数组从零开始,这意味着当我的代码要打印时
exampleArray[11]
int i = 0; // if i is initiated as 0, always use a < or > in the condition
while (i < 10)
System.out.printf("%d ", i++);
int i = 1; // if i is initiated as 1, always use a <= or >= in the condition
while (i <= 10)
System.out.printf("%d ". i++);
Off by one error(有时称为OBOE)会在你试图定位字符串或数组的特定索引(以切片或访问段),或者循环遍历它们的索引时出现。
如果我们以Javascript作为示例语言,索引从零开始,而不是一,这意味着最后一个索引总是比项目长度少一。如果您尝试访问等于长度的索引,则程序可能会抛出
"index out of range" reference error
或
打印undefined
。
当您使用接受索引范围作为参数的字符串或数组方法时,有助于阅读该语言的文档并了解它们是否是包含的(给定索引处的项是返回内容的一部分)或不包含的。以下是一些off by one错误的示例:
let alphabet = "abcdefghijklmnopqrstuvwxyz";
let len = alphabet.length;
for (let i = 0; i <= len; i++) {
// loops one too many times at the end
console.log(alphabet[i]);
}
for (let j = 1; j < len; j++) {
// loops one too few times and misses the first character at index 0
console.log(alphabet[j]);
}
for (let k = 0; k < len; k++) {
// Goldilocks approves - this is just right
console.log(alphabet[k]);
}
常见的一个-off-by-one混淆是因为一些语言从零开始枚举向量(例如C),而其他语言从一开始枚举(例如R)。因此,大小为n的向量x在C中的成员从x [0]到x [n-1]运行,但在R中从x [1]到x [n]运行。
当编写用于循环递增的常见习语时,您还将面临off-by-one挑战:
在C中:
i = (i+1)%n
在R中:
i <- (i-1)%%n + 1