从一开始计数 |
|
在 Lua 核心中,只有一种情况是这样的:解析构造函数,例如 {10,20,30} 。从 1 开始的约定仅由库强制执行,而库不是语言的一部分。--lhf
来自 Lua 手册
好吧,我们来解决这个问题:定义一个代理表指向真实表,通过重写索引元方法,当 i 为正数时,则访问真实表的 i-1。决定何时将负数索引直接映射到真实表,或者是否进一步处理它们。决定如何处理真实表的索引 0。
完成之后,当你需要从零开始计数时,就使用代理表。当你需要使用库函数时,就使用真实表。为了方便记忆,将你的表命名为 'tablename1',代理表命名为 'tablename0'。问题解决了。
我同时是 C++ 和 Lua 程序员,我完全不同意这篇抱怨。从 1 开始计数是“自然”的计数方式。在数学中,每个人也从 1 开始计数。从 0 开始计数只对 C 约定有利,即数组和指针是等价的,并且 a[i] 与 *(a+i) 相同。另外,我认为程序员很容易适应非 C 风格的计数方式。然而,这使得 Lua 对“休闲程序员”更直观。--PeterPrade
+1,因为 Lua 不使用半开区间,而在 C++ 中(尤其是在使用 STL 时,STL 完全使用了这种范围风格)就不会发生这种情况。此外,我同意从 1 开始计数对人类来说是自然的(我从未说过相反的话),但是用 [n, n-1] 来表示零长度区间非常尴尬。通过指示某人地板上的起点,然后指示一个在其后一步的目的地来传达“向前迈出零步”是不自然的。相反,人类可以很容易地通过指示地板上的起点和相同的点作为目的地来理解“向前迈出零步”——这对应于半开区间 [n, n)。--JohnBelmonte
有时半开区间可以实现简洁编码,有时则不能。我见过(并提交过)大量半开和闭合区间都有的“围栏柱”错误。例如,C 数组中最后一个元素的索引比数组长度小一,这可能导致代码中出现各种 -1;可以肯定的是,有消除这些的方法,但我不会说我的 Lua 代码中的 +1 比我的 C 代码中的 -1 多。然而,对于半开区间,存在一个严重的问题:你需要能够表示不在范围内的数量。因此,例如,长度为 256 的字符串的索引类型至少需要是 short,即使每个有效索引都是一个字节。类似地,指向向量末尾的半开区间描述符包含一个不包含在向量存储中的地址,并且可能是一个指向另一个对象的有效指针;例如,这对保守垃圾回收来说很复杂。(至少有一个保守垃圾回收器会故意过度分配以弥补这个问题。)我不是支持这个或那个:两者都是有效的,都有优点,也有缺点。--RiciLake
与从一开始计数一样,我认为闭合区间比半开区间更直观,至少当你谈论零长度区间的特殊情况时。当我使用“直观”时,我的意思是对于一个至少几年没有做过程序员的人来说,它更自然。--PeterPrade
我没有数过,但我想大量的编程语言从零开始计数……而且,当我把 C 作为我的第一门语言来学习时,从一开始计数似乎令人困惑——事实上,我写了一堆糟糕的 Lua 代码,想着从零开始计数——AdrianPerez
虽然从零开始计数有其优点,但我发现从一开始计数更为自然,即使在编程时也是如此。我切换 Lua 和 C 之间没有问题,因为过去我经常在 Visual Basic 和 C 之间切换——匿名
第一个是第一,不是第零 --Kazimir Majorinc
“我正在为一个嵌入式脚本语言评估 Lua,在我看到的到目前为止的一切都非常积极,直到我意识到 Lua 是从索引 1 开始计数的。坏。坏。非常坏。糟糕到足以考虑将整个东西丢掉。至于理由,虽然不应该需要任何理由:长度为 N 的数组的索引自然地映射到模 N 的整数环,它有很多巧妙的性质。例如,当你想要循环访问数组时,你只需要做 index = (index+1) %N。用从 1 开始的索引做同样的事情是件很麻烦的事。此外,它使得将 C 例程绑定到 Lua 变得非常痛苦。”
iterator+1 来访问表索引。--DanHollis?
lua_createtable() API 调用,允许你精确地指定表的数组大小。你也可以指定哈希大小,但这部分始终限制为 2 的幂。如果你创建一个指定了数组大小的表,那么只要你不强制表扩展,它的数组部分就是该精确大小,所以如果你提前知道表的大小,它就会正常工作。在 5.1 中,绑定小原子对象的数组,如 ints,也变得更容易一些,因为可以覆盖 # 运算符,但要使 userdata 真正像 Lua 表一样工作,你需要修改 ipairs 和可能的 unpack 的默认定义。我在 Wiki 上为前者放了一些代码,网址是 GeneralizedPairsAndIpairs,以防它对任何人有用。--RiciLake
lua_Number,默认设置为 double。同样,定义一个常量 lua_FirstIndex(例如)设置为 1 作为默认值,以引用第一个索引,可能会很有趣。Lua 中与数组第一个索引相关的所有部分(例如,ipair、foreachi 等)都应该使用此常量。如果用户想将此常量更改为 0 并重新编译,他/她可以从 0 开始使用 Lua 表,但需要自行承担丢失兼容性的风险。相同的常量也应该可以从 Lua 访问,以便脚本能够移植或独立于数组索引值的第一个值。--Salvador Espana
{[0]=10, 20, 30} 以及库函数的作用就打了折扣。--Ulrich Hoffmann <uho@xlerb.de>
[n,n),而且相邻区间可以很好地加起来——[a,b)+[b,c)=[a,c)——而不会在端点处过度重复。结果区间是 (b-a)+(c-b)=c-a 长,并且任何或两个分量都可以是零长度。此外,还可以考虑“负数区间” [m,n),其中 m>n,并且区间的加法仍然有效,现在甚至可能从非零区间获得零长度区间。现在,考虑数字 a、b 和 x。在 min(a,b)<=x<max(a,b)(半开区间)的意义下,“x 在 a 和 b 之间”的正确表达式是 (a<=x)=(x<b)。如果是闭合区间(...<=x<=...),我们就没有这么好的表达式了。-- Boyko Bantchev
作为一个脚本语言,它的首要任务是易于脚本编写者使用。一个有 5 个元素的数组,最后一个元素是数字 5,肯定比说它以元素 4 结束更容易向休闲脚本编写者解释?
我已经将许多客户端函数接口到我的系统中,以向 Lua 公开它们的工作方式。在许多情况下,零基或一基的问题甚至不适用。例如,许多使用字符串键(在这种情况下问题消失),或者它们不使用任何类型的数组。
如果你希望 Lua 成为一种通用的脚本语言,我绝对不推荐编译时选项,这样一半发布的 Lua 脚本将基于零,一半基于一。这样做你会打开一个噩梦的大门。如果你想接口像 LuaSocket、LuaCom 等,我甚至看不到它如何工作。这些将假定当前的约定,即数组从 1 开始,许多都附带预编译的 Windows 二进制文件。在零基系统上运行它们时,它们要么根本不起作用,要么每个包的作者都必须用测试来充斥他们的代码,以确定基数是什么,这肯定会失去改变它的任何好处。-- NickGammon
double num = luaL_checknumber (L, 1); /* get first item on stack */
因此,将 Lua 与 C 接口的程序员非常熟悉该约定——你必须熟悉。同样,如果你将数组设为零基,你会改变这一点吗?那样的话,你如何从堆栈中获取最后一个元素(当前是 -1)?
上面的一些帖子并没有真正提到它们是否也提到了字符串。例如,字符串中的第一个项
c = string.sub ("ABC", 1) --> "A"
你会把那个也设为零吗?如果是,你如何获取最后一项?-- NickGammon
将 -1 用作最后一个元素的别名与半开区间(即从零开始计数)是完全一致的。参见 Python。事实上,它比闭合区间更一致。暂时考虑一下,列表会环绕,你可以从开头到结尾,再“短路”回来。“0”(列表开头)左边是 -1(列表结尾)。换句话说,你只需从你的位置减去 1 就可以向左移动,这是自然的。如果你从 1 开始列表,它会在开头和结尾之间产生一个奇怪的两个位置的间隙。要向左移动,你需要从你的位置减去一,除非你在位置 1,在这种情况下,你需要减去二。
是的,这是真的。然而,鉴于“事已成定局”现在,我建议那些出于数学或其他原因需要“从零开始计数”方法的应用程序,只需定义自己的 foreachi 函数来解决当前行为。毕竟,现在没有任何东西阻止你把元素放入表的第 0 位。
如果你想让表构造函数从 0 开始,这样做
t = { [0] = "a", "b", "c" } -- a is in position 0, b is in 1 and so on
-- NickGammon
不幸的是 #t 的结果是 2。很多问题源于数字和序数之间的混淆。当索引是偏移量时,0 是数组中第一个项的偏移量。如果我们更严谨地在日常口语中使用数字时保留类型信息,那么争论就会少一些。在数学中,将一个非负整数 n 实现为从 0 到 n-1 的数字集比将其标识为从 1 到 n 的数字集能给出更简洁的公式。-- GavinWraith
mtx[1][1]。序列可能更具可变性[4]。关于数学软件,Mathcad 默认使用 0 索引(尽管可以更改),而 Matlab、Maple 和 Mathematica 使用 1 索引。--DavidManura
#t(或 table.getn)并未定义为返回表中的元素数量。例如
t = { foo = 1, bar = 2 } ; print (table.getn (t)) --> 0
来自 Lua 手册
我的例子与定义一致。#t 返回最后一个项目的索引。
-- NickGammon
很明显我们在这里无法达成一致,但你不认为这“稍微”有点令人困惑吗?“下标 n ... 指向元素 n+1?”至少在 Lua 中,下标 n 指向元素 n。
-- NickGammon
在我看来,从一开始计数很棒。第一个元素是 1,最后一个元素是 -1。无需记住“这不是数学,这不是自然的事,适应它”,就像你在 Python 中处理索引时一样。
-- muntyan
而且 Lua 允许你在整数中存储 0——那么它会带你到哪个元素?
完 全 正 确 -- JeanClaudeWippler
DIM arrayname(5 to 18)在 Forth 中,你也可以从任何你想要的数字开始,比如
HERE 14 ALLOT 5 - CONSTANT arrayname
看来从一开始计数是从一元系统遗留下来的非常古老的传统。我们在发明零时本应摆脱它,但可惜没有。
--Zifre
我们来打个赌。我们去公园找随机的人。我们会给他们看一排三块石头,让他们大声数出来。如果有人说,“零,一,二”,你就赢了。如果每个人都说,“一,二,三”,我就赢了。我猜我总是会赢得这个赌局。没有人从零开始计数,因为它没有意义。当数组的第一个元素用 0 访问时,那个数字代表一个偏移量,而不是索引。问题不在于计数,而在于数组是否应该使用偏移量或索引。
使用偏移量而不是索引当然也有一些优点。但很难反驳这一点:如果数组使用索引,那么“第 n 个”元素就用数字“n”来访问。你想要第四个元素吗?那么用 4。想要第十个?那么用 10。这很有意义,并且有自己的一套很好的性质。不幸的是,许多程序员在这个问题上思想封闭,并且不知何故说服自己相信荒谬的陈述,例如“从 0 开始计数更有意义。”(然后,在他们自己的帖子里,说明他们实际上就像这个星球上的其他人一样,是从一开始计数的。)
我们来打第二个赌。在让人们数石头之后,我们会说:“请指向石头一”。如果有人指向中间的石头,你就赢了。否则,我赢。同样,我看不出我如何会真正输掉这个赌局。-- 匿名
另外,[a,b]+[b,c] 应该变成 [a,b]+[b+1,c] 的说法完全是错误的,因为原始命题需要半开区间,在这种情况下,它看起来是这样的:[a,b)+[b,c)。
因此,这个抱怨完全是错误的,因为 [a,b)+[b,c) 不会重复包含 b。我将纠正那部分,并删除关于 [a,b]+[b+1,c] 的抱怨,因为它没有意义。-- Tim
一些例外
string.gmatch 中,"%0" 表示整个匹配,"%1%2"... 表示第一个、第二个捕获。未改变。
args 表的订阅定义未改变,但请注意,长度运算符现在将返回一个比实际长度大 1 的数字。
math.random 是一个不确定的点。当前状态主要是为了实用性:当提供 1 个参数时返回 [0, n),当提供 2 个参数时返回 [a, b]。
for i=0,3 返回 {0,1,2},for i=3,0,-1 返回 {3,2,1}。
table.remove 的边界情况,其中 i==#t+1 (i==#t base-0) 已被移除。我不理解它。现在它会引发一个错误。其余部分应该是微不足道的,包括一些支持负数索引作为反向索引的情况,它们在很大程度上会像 Python 一样工作。
我反对 base-1 表的另一个原因是:它在转换 JSON(一种广泛使用的数据交换格式)时会引起问题和混淆。
--farter