这里问的一个问题使我想起了我与一位程序员的讨论。他认为,从零开始的数组应该替换为从一开始的数组,因为从零开始的数组是一个实现细节,它起源于数组和指针以及计算机硬件的工作方式,但是这些东西不应在更高层次上得到反映。语言。
现在我不是很擅长辩论,因此除了提供感觉更合适的方法外,我无法提供任何充分的理由坚持使用从零开始的数组。为什么零是数组的通用起点?
#1 楼
我认为我们每个人都不能提供比Edsger W. Dijkstra的文章“为什么编号应从零开始”更强的论据。评论
他确实提出了一些统计数据并使用了数学样式证明。但是我敢肯定有人还会争论。尽管我同意,但我不会。
–Loki
08-12-27 at 0:25
Dijkstra的文章是关于样式的,但是,他的论点是关于简单和易用的... +1。
–paercebal
08-12-31 at 17:17
#2 楼
权威论点好吧...显然,大多数语言,包括最近的语言,都是从零开始的。由于这些语言是由熟练的人编写的,因此您的朋友一定是错的...
为什么要一个?
为什么1会比0更好的起始索引?为什么不2或10?答案本身很有趣,因为它表明了人们捍卫这一想法的艰难过程。
第一个论点是,它更为自然,因为第一个通常至少比所有其他都优先。大多数人...
第一个参数是最后一个索引也是数组的大小...
我通常听到的原因的“质量”给我留下了深刻的印象对于这种论点...甚至还让我想起...
为什么不为零?
信不信由你,原始的公历从-3,-2,-1,1,2,3 ...尝试想象这个问题对西方科学做出了贡献(例如,从1月1日-2到1月2日有多少年才看到原始的公历与诸如减法之类的简单冲突……)。
继续使用基于一的数组是像(我们ll,我会为此而感到沮丧... ^ _ ^ ...),保持21世纪的英里和码...
为什么为零?因为它是数学!
首先(糟糕,抱歉,我会再试一次)
零,零什么都不是,一个是什么。一些宗教文献认为,“一开始,什么都没有。”一些与计算机有关的讨论可能像宗教辩论一样激烈,因此,这一点似乎并没有超出主题范围... ^ _ ^
首先,使用基于零的数组并忽略其第零值比使用基于一的数组并四处寻找其零值要容易得多。这个理由几乎和以前一样愚蠢,但是后来,支持基于一个的数组的原始论证也是一个谬论。
其次,让我们记住,在处理数字时,机会很高一时或一刻地处理数学,当您处理数学时,很可能您没有心情愚蠢的黑客绕过过时的惯例。以一为基础的表示法也困扰着数个世纪的数学和日期,并且通过从错误中吸取教训,我们应该努力在面向未来的科学(包括计算机语言)中避免使用它。
第三,关于计算机语言数组的束缚在硬件上,分配一个由21个整数组成的C数组,然后将指针向右移动10个索引,您将拥有一个自然的[-10至10]数组。这对于硬件而言是不自然的。但这是为了数学。当然,数学可能已经过时了,但是我上次检查时,世界上大多数人都认为不是。
四,正如其他地方已经指出的那样,即使对于离散位置(或距离减小为离散值),第一个索引将为零,例如建筑物的地板(从零开始),递减的倒数计数(3、2、1,零!),地面高度,图像的第一个像素,温度(零开尔文) (水冻结温度为273 K)为绝对零或零摄氏度)。实际上,唯一真正开始的是传统的“第一,第二,第三等”方式。迭代符号,自然而然地将我引向了下一点...
接下来的五点(自然而然地接在后面)是,除非索引本身具有内在值,否则不应通过索引而是通过迭代器来访问高级容器。我很惊讶您的“高级语言”倡导者没有提到这一点。在索引本身很重要的情况下,您可以将一半时间押在与数学有关的问题上。因此,您希望您的容器对数学友好,而不是像从1开始的“您的旧公历”那样禁用数学,并且需要反省的技巧才能使其正常工作。
结论
您的程序员同伴给出的论点是谬论,因为它不必要地将口头/书面语言习惯与自然地模糊的计算机语言(您不希望您的指令模糊化)联系在一起,并且由于错误地归因于硬件原因他希望解决这个问题。随着语言的抽象化程度不断提高,她希望说服您,从零开始的数组已成为过去。
从零开始的数组由于基于数学原理而从零开始相关原因。不是出于硬件相关的原因。
现在,如果这对您的同伴程序员是一个问题,请让他开始使用真正的高级结构(例如迭代器和foreach循环)进行编程。
评论
零摄氏度为273,15K;)
– Pim Jager
08年12月27日在9:26
我知道(我有物理学硕士文凭),但是我觉得小数点玩法的意义要小于幽默的一面,我试图用... ^ _ ^ ...
–paercebal
08年12月31日在17:08
您的段落标记为“零,第一,第二,第三,四个,五个”。为了保持一致,您应该使用基数(“零,一,二,三,四,五”)或序数(“零,第一,第二,第三,第四,第五”)。 :-)
–ShreevatsaR
2010-2-16在21:35
同样,对于我们生命的第一年,我们不是一岁而是零岁
– ChristV
2010年6月11日在11:42
@Nikita Rybak:令人惊讶的是,您错过了所有评论员在您之前看到的内容:当然,Bill the Lizard的答案是正确的。这就是为什么我给他投票+1,这就是为什么它被选为问题的最佳答案的原因。我的答案更多是关于嘲笑基于1的数组背后的谬误原因,并提供一些具体的案例,其中基于1的数组会令人讨厌。尽管如此,令我惊讶的是,即使考虑到混杂着讽刺意味的原因,您也发现“没有一个令人信服的人”。
–paercebal
10-10-20在15:13
#3 楼
半开间隔时间很好。如果您要处理0 <= i < lim
并想扩展n
元素,则新元素的索引范围为lim <= i < lim + n
。从零开始的数组工作使拆分或连接数组或对元素计数时使算术更加容易。人们希望更简单的算法可以减少栅栏错误。 #4 楼
某些类型的数组操作会因基于1的数组而变得复杂,而对于基于0的数组则更为简单。我曾经做过一些数值分析编程。我正在研究用FORTRAN和C ++编写的压缩稀疏矩阵算法。
FORTRAN算法有很多
a[i + j + k - 2]
,而C ++有a[i + j + k]
,因为FORTRAN数组是基于1的,而C ++数组基于0。评论
我同意。我发现基于1的数组很有用的唯一时刻是当我想为空项目索引腾出空间时。例如,如果我有一个对象数组,并使用它们的索引作为句柄,并且想要有一个空句柄。
–Fabio Ceconello
08年12月26日在21:36
我也遇到了不必要的基于1的数组的麻烦,以我有限的经验,基于0的数组总是为数组索引生成更清晰的代码,但很少见。
– Eric Scrivner
08年12月26日在22:04
如果FORTRAN和C ++索引各自的偏移量仅偏移1,那么它们之间的差值将如何相差2?另外,为什么要减2?如果FORTRAN是基于1的,那么您不添加2(或1)吗?
– RexE
08-12-26 at 23:28
@RexE:这就是它的工作原理,这就是为什么基于1的数组如此复杂。
–杰伊·巴祖兹(Jay Bazuzi)
08-12-30在20:44
@RexE:假设您模拟一个平面的3d阵列。然后,以0为底,元素(0 0 0)对应于平面数组中的元素0。 OTOH,如果基于1,则元素(1 1 1)对应于平面数组中的元素1:1 + 1 + 1-2。
– rlbond
09年5月11日在2:36
#5 楼
数组中的索引实际上不是索引。它只是一个偏移量,它是距数组开头的距离。第一个元素在数组的开头,因此没有距离。因此,偏移量为0。评论
对于当今大多数设计的语言,这实际上是实现细节,不应在语言中出现(除非有其他更好的理由这样做)
–詹斯·绍德(Jens Schauder)
09年9月6日于19:42
#6 楼
原因不只是历史原因:C和C ++仍然存在并且被广泛使用,并且指针算术是使数组从索引0开始的非常有效的原因。对于其他缺少指针算术的语言,无论是第一个还是第一个元素位于索引0或1更像是约定俗成的约定,而不是其他任何约定。
问题是,使用索引1作为其第一个元素的语言并不存在,而且通常必须与通常用C或C ++编写-C或C ++ ...
VB及其派生的样式因数组从0或1开始而受苦,并且长期以来一直是问题的根源。
底线是:您的语言认为第一个元素索引并不重要,只要它在整个过程中保持一致即可。问题在于,将1作为第一个索引会使在实践中更难使用。
评论
同意一致性很重要,除非您完全避免完全避免使用低级代码(包括C / C ++),否则使用基于1的数组只会带来麻烦。
– Shog9
08年12月26日在4:51
当我们讨论这个问题时,您会问:您是否曾经以非平台特定的方式使用低级代码?换句话说,您总是在一个或另一个平台上,并且您必须知道哪个,对不对?
–丹·罗森斯塔克(Dan Rosenstark)
08年12月26日在5:00
就像认为VB .NET通常受到不公正对待的人一样,我不得不说VB .NET在数组上的实践很糟糕。他们将差异分开,并使之更加混乱:数组从0开始,但是Dim a作为Integer(5)创建具有6个位置的数组。理由似乎是,拥有额外的位置要比解决数组长度过长的错误要好。不幸的是,在这方面(以及其他问题,例如And和Or被按位划分),它们满足了许多VB6程序员的要求,这些程序员最终都没有使用VB .NET。
– Kyralessa
09年8月13日在17:38
@Kyralessa:不,其基本原理是要向后兼容VB6(自动升级助手…),尽管他们很清楚该表示法是违反直觉且容易出错的。另一方面,“与”或“或”与VB6无关,它是VB类型语言的唯一逻辑解决方案。您的逻辑操作确实有AndAlso和OrElse。
–康拉德·鲁道夫(Konrad Rudolph)
09年9月6日在19:36
并且和或按位与VB6有关,因为它们在VB6中是按位的。丑陋的运算符AndAlso和OrElse应该按位进行运算,因为按位运算比逻辑运算要少得多。由于“向后兼容性”,语言上留下了许多丑陋的疣,例如即使默认情况下,ByVal仍会被粘贴在各处。
– Kyralessa
2010年3月30日20:21在
#7 楼
从零开始的数组起源于C甚至汇编程序。使用C,指针数学基本上像这样工作:数组的每个元素占用一定数量的字节。一个32位整数(显然)是4个字节;
数组的地址被数组的第一个元素占用,随后的元素在其后的大小相等的连续块中占据。
举例说明,假设
int a[4]
位于0xFF00,地址为:a [0]-> 0xFF00;
a [1]-> 0xFF04;
a [ 2]-> 0xFF08;
a [3]-> 0xFF0C。
因此,对于从零开始的索引,地址的计算很简单:
元素地址=数组地址+索引* sizeof(type)
实际上C中的表达式都是等效的:
a [2];
2 [a];和
*(a + 2)。
使用基于一个的数组,其数学(曾经如此)稍微复杂一点。
所以原因在很大程度上历史。
评论
最初的问题已经指出“基于零的数组是一种实现细节,它源于数组和指针以及计算机硬件的工作方式,但是这类内容不应反映在高级语言中。”
–poulejapon
08-12-26 at 4:34
值得一提的是,允许基于N的数组的语言通常会生成具有自动以零运行时间成本计算的数组“偏移”的代码。
–罗迪
08年12月26日在22:26
#8 楼
如果使用从零开始的数组,则数组的长度是有效索引的集合。至少,这就是Peano算术所说的:0 = {}
1 = 0 U {0} = {0}
2 = 1 U {1} = {0,1}
3 = 2 U {2} = {0,1,2}
...
n = n-1 U {n-1} = {0,1,2...n-1}
从某种意义上说,它是最自然的表示法。
#9 楼
因为C与数组和指针之间存在很强的相关性,所以起始索引1会让您以后头痛
#10 楼
堆是基于1的阵列的优点之一。给定索引i,i的父级和左子级的索引为PARENT
[i] = i÷2 LCHILD
[i] = i×2 但仅适用于基于1的阵列。对于基于0的数组,您有
PARENT
[i] =(i + 1)÷2-1 LCHILD
[i] =(i + 1)×2- 1 ,然后具有以下性质:i也是该索引的子数组的大小(即,索引在[1,i]范围内)。
但最后没关系,因为您可以通过分配比平常多的一个元素,而忽略第零个元素,从而使基于0的数组成为基于1的数组。因此,您可以选择在适当的时候获得基于1的数组的好处,并在几乎所有其他情况下保留基于0的数组以进行更清晰的算术运算。
#11 楼
我的感觉是,它是完全任意的。基于零或一的数组没有什么特别的。自从Visual Basic解放了自己(大多数情况下,有时我在Excel中做了一些小事情)以来,我还没有使用基于1的数组,而且...是相同的。事实是,如果您需要数组的第三个元素,那么它只是一个实现细节,称为3或2。但是,处理数组的99%的工作只对两个绝对要点感兴趣:第一个元素和数量或长度。同样,只是实现细节,第一个元素称为零而不是一个,或者最后一个元素称为count-1或count。编辑:有些回答者提到从1开始的阵列更容易出现击剑错误。以我的经验,现在考虑一下,这是事实。我记得在VB中思考过,“这会起作用或会因为我被淘汰而失败。”在Java中,这永远不会发生。尽管我以为自己会好起来,但一些回答者指出,从0开始的数组会导致更好的算术,甚至在您不必处理较低级别的lang时也是如此。
评论
在PHP中,大多数在字符串函数内部的搜索都在未找到时返回FALSE。不是-1。
– jmucchiello
08年12月26日在7:42
您正在将计数与最后一个元素的索引混淆。无论使用基于零的数组还是基于一的数组,一个空数组的计数始终为0。基于整数的数组的优点是,计数是最后一个元素的位置(但这仅是唯一的优点)。
–法律
08-12-26 at 11:29
关于这两点,都是正确的:后半部分已删除,因为从零开始的数组或从一开始的数组都相同:如果元素为零,则计数为0。
–丹·罗森斯塔克(Dan Rosenstark)
08-12-26 at 17:50
我的意思是回答的最后一半...
–丹·罗森斯塔克(Dan Rosenstark)
08-12-26 at 17:52
#12 楼
作为一个十岁以上的C / C ++程序员,在Pascal和Delphi方面有着非常深厚的背景,我仍然想念Pascal强大的数组绑定和索引类型检查,以及随之而来的灵活性和安全性。一个明显的例子是数组数据保存每个月的值。Pascal:
Type Month = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec);
Var Days[Month] of integer;
...
if Year mod 4 = 0 then // yes this is vastly simplified for leap years and yes i don't know what the comment marker is in pascal and no i won't go look it up
Days[Feb] := 29
else
Days[Feb] := 28;
用C语言编写类似的代码,而无需使用+/- 1或“魔术数字”非常具有挑战性。注意,像Days [2]和Days [Jan + Dec]这样的表达式根本不会编译,这对于仍在C或汇编语言中思考的人们来说可能是残酷的。
我不得不说Pascal / Delphi语言的很多方面我都不会错过,但是相比之下,基于C零位的数组似乎只是“愚蠢”。
评论
可能值得注意的是,您的算法在2100年之前是不正确的。en.wikipedia.org/wiki/Leap_year#Algorithm
– Mark A. Nicolosi
08年12月27日在2:59
我知道;-)但是,这在2000年是正确的。我只是在玩“发现学步车” ...
–罗迪
08年12月27日在8:51
当场学徒!大声笑。
– jcollum
09年8月13日在17:31
是。避免整个问题,根据需要建立阵列。
–Loren Pechtel
09年9月6日于19:42
如果您的平均Pascal编译器在生成机器代码时分配Jan = 0,Dec = 11,我不会感到惊讶:
–克里斯蒂安·海特(Christian Hayter)
09年9月28日在20:20
#13 楼
它以0而不是1开头的原因是,您可以将偏移量视为该元素距数组内存的起始位置有多远。这并不是说给我第0个元素,而是说给我一个从一开始就是0个元素的元素。另一种看待它们的方法是(在大多数情况下)等效:
array[n]
*(array + n)
标准不符合的原因之所以会被更改是因为C已经存在了大约40年。没有令人信服的理由对其进行更改,如果进行了更改,则依赖于数组开头为0的所有现有代码都将被破坏。
评论
实际上,您可以在C语言中将array [n]重写为n [array]。这样做不是一个好主意,令人困惑!但这是合法的(至少要达到C89),因为上面的标识和加法是可交换的。
–研究员
2010年7月25日在16:37
这是一种疯狂的编写方式-如果您在必须维护的任何代码中看到的内容,那将是一个巨大的警告信号。值得庆幸的是,我还没有遇到过...)
–丹尼斯·蒙西(Dennis Munsie)
10年7月25日在19:24
#14 楼
包含一些原始位置/相对位置信息的代码使用从0开始的数组更加清晰。例如:
将矢量复制到较大矢量中定义位置的代码很麻烦。从1开始的数组:
function copyAtPos (dest, vect, i):
for i from 1 -> vect.length do
dest[pos+i-1] = vect[i]
与从0开始的数组相对:
function copyAtPos (dest, vect, i):
for i from 0 -> vect.length-1 do
dest[pos+i] = vect[i]
如果开始写大卷积公式,这是必须的。
#15 楼
为什么不2或3或20?这并不比从零开始的数组更容易理解或基于基于1的数组。为了切换到基于1的数组,那里的每个程序员都必须重新学习如何使用数组。此外,当您处理现有数组中的偏移量时,它也更有意义。如果您从数组中读取了115个字节,则知道下一个块从115开始。依此类推,下一个字节始终是您所读取的字节的大小。从1开始,您需要一直加一。
有时您甚至需要处理数组中的数据块,即使是没有“ true”指针算法的语言也是如此。在Java中,您可以在内存映射文件或缓冲区中存储数据。在这种情况下,您知道块i的大小为* i。对于基于1的索引,它将位于块* i + 1。
使用基于1的索引,很多技术都需要在整个位置+1。
评论
为什么不2或3或20?因为0是加法身份,而1是乘法身份。他们最有意义。
–亚当·克鲁姆
2010年6月17日在20:16
#16 楼
使用基于1的数组,将一维数组转换为多维数组:int w = 5, h = 5, d = 5;
int[] a1 = new int[w * h * d], new a2 = int[w,h,d];
for (int z = 1; z <= d; z++)
for (int y = 1; y <= h; y++)
for (int x = 1; x <= w; x++)
a1[x + (y - 1) * w + (z - 1) * h] = a2[x,y,z];
请注意,您的y和z索引均基于0(Y- 1,z-1),即使您的数组基于1。在某些情况下,您无法避免从0开始的索引。为了保持一致性,为什么不总是使用基于0的索引?
#17 楼
为什么要数组从一个开始?当您说
a[x][y]
时,编译器将其转换为:a+(x*num_cols+y)
。如果阵列从1开始,则将变为a+(x*num_cols+y-1)
。每当您要访问数组元素时,这都是一个额外的算术运算。您为什么要减慢程序速度?评论
实际上,它必须变成+(((x-1)* num_cols)+ y-1)-x和y都将从1开始。
–丹尼斯·蒙西(Dennis Munsie)
2010年7月25日在19:25
#18 楼
我将在这里走出一条弯路,提出一些与整数“键控”数组不同的建议。我认为您的同事正在建立一个“集合”的一对一映射。我们总是从1开始计数的物理世界。我能理解这一点,当您不做任何花哨的事情时,在软件与物理世界之间一对一映射时,很容易理解一些代码。
我的建议
不要为存储的内容使用基于整数的数组,而应使用其他种类的字典或键值对。由于您不受任意整数的束缚,因此它们可以更好地映射到现实生活。它有它的位置,由于在软件和物理世界之间映射概念1到1的好处,因此我建议您尽可能多地使用它。
即
kvp['Name Server'] = "ns1.example.com";
(这只是百万个示例中的一个)。Discaimer
在使用基于数学的概念时,这绝对不起作用,主要是因为数学是更接近计算机的实际实现。在这里使用kvp集将无济于事,但实际上会使事情变得混乱,并使问题更加严重。我还没有考虑过在某些情况下某些东西可能会更好地作为kvp或数组工作的想法。
最终的想法是在有意义的地方使用从零开始的数组或键值对,请记住,当您只有锤子时,每个问题开始看起来像钉子...
#19 楼
就个人而言,一个参数是将数组索引视为偏移量。可以说它是第一个元素,但是第一个元素相对于数组原点的偏移为零。这样,取数组原点并加零将产生第一个元素。
因此,在计算中,加零以找到第一个元素比加一个然后删除一个元素更容易。
我认为做过一些低级工作的人总是以零为底。而且那些开始或习惯于更高层次的人经常不使用算法编程,他们可能希望使用基本的系统。也许我们只是对过去的经验有偏见。
评论
确实-从根本上讲,这是一种底层语言的约定。
–游泳池
2010-2-3在15:56
#20 楼
使用基于0的索引而不是基于1的索引的仅有两个(非常)严肃的理由似乎是在避免重新培养大量程序员并保持向后兼容性。我没有看到任何其他严肃的论点在您收到的所有答案中都基于基于1的索引。
实际上,索引自然是基于1的,这就是为什么。
首先,我们必须问:数组来自哪里?他们有真实世界的对等物吗?答案是肯定的:这就是我们如何在计算机科学中对向量和矩阵进行建模。但是,向量和矩阵是在计算机时代之前就使用基于1的索引的数学概念(并且如今仍主要使用基于1的索引)。
在现实世界中,索引是1- bases。
正如Thomas上面所说,使用0基索引的语言实际上是使用偏移量而不是索引。使用这些语言的开发人员会考虑偏移量,而不是索引。如果事情清楚地说明了,那不是问题,但事实并非如此。许多使用偏移量的开发人员仍在谈论索引。而且许多使用索引的开发人员仍然不知道C,C ++,C#,...使用偏移量。
这是一个措辞问题。
(有关Diskstra的论文-正是我在上面所说的:数学家确实使用基于1的索引,但Diskstra认为,数学家不应该使用它们,因为这样某些表达式会很丑陋(例如:1 <= n <= 0)。 ,不确定他是正确的-为了避免那些异常的空序列,进行这种范式转换似乎很麻烦,但效果不明显...)
评论
数学家并不总是使用基于1的索引。我已经看到x0花费了很多时间作为序列的初始值。这取决于哪个更方便。
–亚当·克鲁姆
2010年6月17日在20:13
#21 楼
您是否曾经被真正提到1900年代的“ 20世纪”所困扰?好吧,这是您在使用基于1的数组时一直处理的繁琐事情的一个很好的类比。考虑一个常见的数组任务,如.net IO.stream读取方法:
int Read(byte[] buffer, int offset, int length)
这是我建议您说服自己的基于0的数组更好的方法:
在每种索引样式中,编写一个支持读取的BufferedStream类。您可以为基于1的数组更改Read函数的定义(例如,使用下限而不是偏移量)。不需要花哨的东西,只需使其简单即可。
现在,这些实现中哪一个更简单?哪一个位置的偏移量为+1和-1?我也这么想。实际上,我认为,索引样式无关紧要的唯一情况是何时应该使用非数组的东西,例如Set。
评论
不好的类比将整数逻辑与浮点数混淆了。
–游泳池
2010-2-3在15:59
#22 楼
这是因为数组的构造方式。让他们从一个开始就没有多大意义。数组是内存中的基址,大小和索引。要访问第n个元素,它是:base + n * element_size
所以0显然是第一个偏移量。
#23 楼
实际上,有几种不同的方法可以实现此目的:基于0的数组索引
基于1的数组索引
基于0或1的数组(例如VB 6.0) ...这真是太可怕了。
最终,我认为语言使用0还是1都不重要。
但是,我认为最好的选择是使用0基于数组的原因很简单,因为大多数程序员都习惯于该约定,并且它与绝大多数已编写的代码一致。
唯一真正出错的方法是与Visual Basic不一致。我当前正在维护的代码库分为基于0和1的数组。而且很难找出哪个是哪个。这会导致烦人的for循环:
dim i as integer, lb as integer, ub as integer
lb = LBound(array)
ub = UBound(array)
for i = lb to ub
'...
next
评论
哈哈哈,我记得那个吸吮的人...
–丹·罗森斯塔克(Dan Rosenstark)
08-12-26 at 13:58
我想我记得甚至有以负数开头的数组。我远离VB的众多原因之一。
–布拉德·吉尔伯特(Brad Gilbert)
08-12-26 at 17:11
#24 楼
在讨论线性集合中某个项目的位置时,零是很自然的。想一堆装满书的书架-第一本书位于与书架的侧壁齐平的位置-位置为零。
所以我想这取决于您是否将数组索引视为查找事物或引用事物的一种方式。
#25 楼
我更喜欢基于0的索引,因为模(以及用于模的AND运算符)对于某些值总是返回0。我经常发现自己使用这样的数组:
int blah = array[i & 0xff];
当使用基于1的索引时,我经常会弄错这种代码。
#26 楼
如果不编写许多基于数组的代码(例如字符串搜索和各种排序/合并算法)或在单维数组中模拟多维数组,则很难保护0基。 Fortran是基于1的,因此您需要花费大量精力才能正确完成这种代码。但是它远远超出了此范围。能够思考事物的长度而不是其元素的索引是非常有用的心理习惯。例如,在制作基于像素的图形时,将坐标视为落在像素之间而不是落在像素上要清晰得多。这样,一个3x3矩形将包含9个像素,而不是16个像素。
一个更牵强的示例是在解析或打印表小计中的超前思想。 “常识”方法说:1)获取下一个字符,令牌或表行,以及2)决定如何处理它。前瞻性方法表示1)假定您可以看到它,并确定是否需要它,以及2)如果您确实想要它,则“接受”它(它使您可以看到下一个)。然后,如果您写出伪代码,则要简单得多。
另一个示例是如何在无选择的语言(例如MS-DOS批处理文件)中使用“ goto”。 “常识”方法是将标签附加到要完成的代码块上,并对其进行标签。通常,更好的方法是将标签放在代码块的末尾,以跳过它们。这使它“结构化”,并且易于修改。
#27 楼
就是这样,已经有很多年了。更改甚至辩论,就像更改或辩论更改交通信号灯一样没有意义。让我们使blue = stop,red = go。研究C ++数值食谱中随着时间的变化。他们曾使用宏伪造基于1的索引,但在2001年版中放弃了并加入了这一行。在其网站www.nr.com
BTW上,可能有很多启发性的材料对此背后的原因进行了说明,但令人烦恼的是指定数组之外的范围的变体。例如:python vs. IDL; a [100:200] vs a [100:199]获得100个元素。只是要学习每种语言的怪癖。更改一种语言以使其匹配另一种语言会引起这种咬牙切齿的作用,而不能解决任何实际问题。
#28 楼
我更喜欢基于0的数组,因为正如其他人所提到的,它使数学变得更容易。例如,如果我们有一个包含100个元素的一维数组,它们模拟一个10x10网格,那么r行中该元素的数组索引i是col c:0-based: i = 10 * r + c 1-based: i = 10 * (r - 1) + c
,给定索引i,返回到行和列是:
0-based: c = i % 10 r = floor(i / 10) 1-based: c = (i - 1) % 10 + 1 r = ceil(i / 10)
鉴于使用基于1的方法时,上述数学显然更复杂数组,选择基于0的数组作为标准似乎合乎逻辑。
但是,我认为有人可以声称我的逻辑有缺陷,因为我认为会有理由在其中表示2D数据。一维数组我在C / C ++中遇到过许多这样的情况,但是我必须承认,执行这样的计算需要某种程度上取决于语言。如果数组确实一直在为客户端执行所有索引数学运算,则编译器可以简单地在编译时将基于M的数组访问转换为基于0的访问,并向用户隐藏所有这些实现细节。实际上,任何编译时常量都可以用于执行相同的操作集,尽管这样的构造可能只会导致难以理解的代码。
也许更好的论据是将数组的数量减到最少具有基于1的数组的语言的索引操作将要求使用上限函数执行整数除法。但是,从数学角度来看,整数除法应返回d个余数r,其中d和r均为正。因此,应使用从0开始的数组来简化数学。例如,如果要生成包含N个元素的查找表,则值x的当前值之前的最接近的索引会be(大约忽略结果舍入前为整数的值):
0-based with floor: floor((N - 1) * x / xRange) 1-based with floor: floor((N - 1) * x / xRange) + 1 1-based with ceil : ceil ((N - 1) * x / xRange)
请注意,如果使用四舍五入的标准约定,则基于1的数组需要附加操作,这是不希望的。这类数学无法被编译器隐藏,因为它需要对幕后发生的事情有较低的了解。
评论
在拥有支持多维数组的高级语言之前,这是一个很好的理由。
–游泳池
2010-2-3在16:00
#29 楼
我敢打赌,程序员只是在日常思考中对基于0的数组的反直觉感到恼火,并且在争辩一种更直观的描述数组的方法。具有讽刺意味的是,作为人类,我们花了很多时间来提出“类”,以便我们可以在代码中以更人性化的方式描述事物,但是当查看0对1数组时,我们似乎陷入了困境逻辑本身。就计算机而言,从数学上讲0可能会更好,但是我觉得这里有一点遗漏。如果我们想以更人性化的方式(例如,类)来描述事物,为什么我们不希望在语言的其他部分也是如此?难道这不是同样逻辑或有效(或对此事优先考虑...),从而使一种语言更容易为人类所理解和使用,因此,从广义上讲,它不那么容易产生逻辑错误,更容易发生。加快生产可用作品的速度。 PHP示例:
array(1 => 'January', 'February', 'March');
根据我们的要求给出了一个基于1的数组。
为什么没有规范:
array('January', 'February', 'March');
,例外是:
array(0 => 'Value for scenario where 0 *has* to be used as the key',
'value2', 'value3');
对于PHP,我的赌注是80%的赔率数组是默认的语法方式,可以减少实际使用案例中的逻辑错误,或者至少不会平均导致更多的逻辑错误,同时使编码器更容易更快地生成可用代码。记住,我假设在需要时仍然可以选择array(0 =>'value'),但同时也假设在大多数情况下,更接近于真实世界的描述是可行的。 />
从该角度来看,这听起来确实没有太过牵强。当使用界面时,无论是操作系统还是程序员的语言,我们都更接近人类的思维和习惯来设计界面,在大多数情况下我们会更快乐,而人与计算机之间的误解也就更少了(人类逻辑-错误),更快的生产速度等。如果在现实世界中有80%的时间在进行列表或计数时我用1来描述事物,那么计算机理想情况下应该以较少的信息来理解我的意思,或者与我通常的描述方式有所不同。简而言之,我们可以对现实世界建模的越近,抽象的质量就越高。因此,他想要的绝不是愚蠢的,因为这是最终目标,并且将证明需要更多抽象。计算机仍然可以最终将其视为基于0的数组的特殊用法。只要计算机以一种更简单,更直观的方式描述我想要的东西,并且随着时间的推移减少错误,我就不必在乎计算机如何解释它。
那是我的两分钱。我严重怀疑他在说什么或被解释为他的意思。他可能的意思是,“我讨厌用一种不太直观的方式告诉计算机我想要什么。” :)我们不是所有人吗?哈哈。
#30 楼
如果您在编写“自己的”代码时要小心,则有可能。您可以假设所有n> = 0的索引都从n开始,并进行相应编程。关于标准,Borealid有一个很好的论据。
评论
在n元素数组中,元素'n'不存在。一个n元素数组具有仅从0到n-1的成员。因此,如果我们有一个从1开始的数组,这样n个元素的数组实际上代表了该数组中存在的n个元素,那就更好了。我喜欢从零开始的数组,因为字节的第一位是2 ^ 0,而不是2 ^ 1。有时候这对我有帮助:)
例如,如果有人查看此列表,例如en.wikipedia.org/wiki/…,就会注意到大多数领域特定语言的索引从1开始,而大多数来自cs思维学校的语言的索引都从0开始。 ,他将注意到,在这个问题上已经进行了许多愚蠢的讨论,试图谈论两者中的任何一个以改变其方式可能是毫无意义的。为了捍卫“ 1”,世界上大多数人都使用那个。大多数程序员使用“ 0”。大多数人不了解程序员,所以会让您觉得...