数组下标从 1
开始更符合常人直觉,如果受限于各种原因实在难以改用从 1
开始的话,能不能给出一个开关选择?
数组在计算机内存中通常是一块线性的存储区域,使用从 0 开始的数组下标可以简化偏移量的计算。事实上,大多数程序语言的数组下标都是从 0 开始的。不过尽管如此,在 MoonBit 中您可以很容易地构造一个类型,具有和 Array
一样的特征,但是从 1 开始索引。
是的,大多数编程语言的数组下标都是从0开始的,但是几乎每个编程初学者都会疑惑为什么要这么设计,技术发展到如今,这种事情本来就可以交给编译器来完成以减少人的心智压力。同理,如果你愿意的话,你完全可以构造一个类型使其具有和Array
一样的特性,但是从0开始索引。
数组下标应该从何处开始是一个非常有意义的话题。然而,我们认为和大多数编程语言保持一致能够使得 MoonBit 更容易学习。如果数组下标从 1 开始,相信同样会让很多 MoonBit 的学习者感到困惑。
可以试试这个:New Array
从 0 开始性质是更好的。比如你要从数组中间的某个位置开始遍历一个数组:
var i = 0
while i < len, i = i + 1 {
let elem = array[(initial_index + i) % len]
}
如果 index 从 1 开始,那就得写成 ((intial_index + i - 1) % len) + 1
,相当于转到 0 开始的 index 再转回去。
类似地,如果从 0 开始,那么 (-1) % len = len - 1
,也就是说 -1
是最后一个元素,更符合直觉(有些地方 (-1) % n = -1
,改成 ((-1) + n) % n
就行)。
此外,考虑把一个矩阵存到一维数组里的情况,此时如果从 0 开始,第 i 行第 j 列的 index 是 i * width + j
。如果从 1 开始,则是 (i - 1) * width + j
,更复杂。
所以 index 从 0 开始的性质比从 1 开始更好,很多地方从 0 开始能写得更优雅。
可以自己包装一下数组然后实现一个自定义的op_get
呃,你这个就很没有说服力了。
如果你习惯从1开始,你就不会var i = 0
而是 var i = 1
,-1代表最后一个也是语法糖,非通用,而且最后举的矩阵转一维数组,那么反过来的情况呢?julia
和Matlab
这种经常和数组和矩阵打交道的科学计算语言选择用1不是没有道理的,事实上在经过各种讨论后从1开始是更方便且书写优雅的。用0更多是历史遗留的习惯。
当然MoonBit
不是科学计算语言,选0也无所谓。
var i = 0
不是重点。重点是后面第三行的 (initial_index + i) % len
那里。如果你从 1 开始这里就得先转成从 0 开始,求模,再转回去。
科学计算语言可以从 1 开始是因为,它们提供了大量高层的 API,用户很少需要自己去遍历矩阵做操作。这些语言的内部实现则会是从 0 开始更加优雅。
供参考:Why do Julia arrays use 1-based indexing? ,里面有个更早的讨论长帖。
刚试着在在线例程里找数组操作,比如这个取和:Moonbit Lang ,while i < ary.length()
就要变成 while i <= ary.length()
。下标从1开始的一个可能的好处是,末尾的序号和数组长度相同,在这种需要决定<还是<=的时候也许会少点思考负担。
末尾序号和长度相同是一个优势。
但另一方面,从 0 开始也没有负担。只需要记住四个字:“左闭右开” 就行。除了数组下标之外的很多地方(几乎所有)左闭右开表示区间都是最常见的做法,而从 0 开始和这个习惯是一致的(数组下标的区间是 [0, len)
)
让我想起一个同事教孩子数学时,问的一个问题:数一数屋里有几只大象?一只也没有哦!似乎从 0 开始也是很符合直觉的,而数组下标本质上是元素内存的【起始地址】,也就是数每一只“大象”之前的计数,也就是 0 → 大象 → 1 → 大象 → 2 → 大象 → 3 …
建议看看上面 julia 的gg讨论组的长帖。恰巧 此楼 提到数数:
1-based indexing is actually far more natural. Do “normal” people count 0,1,2 ,…? No, they count 1,2, 3,…
嗯嗯,已读,谢谢!这个讨论很有趣,我们这里的也是。因为我也是普通人,从小学的就是从 1 开始数数,同事的说法才会令我印象深刻——原来还能这么想这个问题。当然他自己也是开发者,所以也可能是受“历史遗留的习惯”影响。
从1开始你不用牢记四个字,[]
就是闭区间,这不更符合眼见即所得?长度你更不用每次都加一减一。所以很不明白你举的种种优势…
看语言开发者的选择了,是要让用户更多地像计算机一样思考还是更多贴合人本身自然的思考方式了。
闭区间是肯定不行的。两个闭区间拼接在一起会重叠。先遍历 [1, 2]
,再遍历 [2, 3]
,那 2
会被遍历两次(不管你从 0 开始还是从 1 开始)。类似地,开区间也不行,会漏东西。只有 左开右闭 和 左闭右开 是性质好的,然后这两个里面,左闭右开是占绝对主流的做法(这个纯粹是历史原因,但现状就是这样)。注意这个问题和从 0/1 开始没有关系,左闭右开成为主流也不是因为从 0 开始。
不是,这主要是习惯问题。你一直在用从0开始的习惯来思考从1开始的情况,你遍历完[1,2]再遍历[3,4]啊,你遍历[1,2] 再遍历[2,3]都不需要运行,直接告诉你会重复了… 只要你不是从1开始,碰到数组操作,你都得先把脑子里自然想到的处理都先减去1,你想取数组里第三个元素,哦,我得写2,下标语义和指针偏移语义混用,你能管这叫方便?从0还是从1开始,就看你们开发者从什么角度出发,别的语言都这样,你也这样从0开始完全没问题。
咱俩其实不用争,从0开始与从1开始的语言我都用过,我个人觉得从1开始是用了就回不去的那种。我只是提个建议,毕竟有现代语言已经选择从1开始了,moonbit要不要跟看开发者团队怎么考量。
暴论来了:建议下标从0.5开始【
还在纠结应该从0还是从1开始数数吗?
换句话说,程序内整数类型也分开基数与序数类型,注意,表示一个区间的两侧边界时,又应该用基数了。