从一开始计数 |
|
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 时,它在整个过程中都使用这种范围样式),这种情况不会发生。此外,我同意从一开始就计数对人类来说是自然的(我从未说过相反的话),但是用 [n, n-1]
表示零长度范围非常笨拙。用地板上的一个起点,然后是一个落后一步的目的地来告诉某人“向前迈出零步”是不自然的。相反,人类可以很容易地理解“向前迈出零步”,方法是在地板上指示一个起点,以及一个相同的终点——对应于半开范围 [n, n)
。--JohnBelmonte
半开区间有时可以使代码更简洁,有时则不然。我见过(也犯过)很多关于半开区间和闭合区间的边界错误。例如,C 数组中最后一个元素的索引比数组长度小 1,这会导致代码中出现各种 -1
;当然,有一些方法可以消除这些错误,但我不会说我的 Lua 代码中 +1
的数量比我的 C 代码中 -1
的数量更多。然而,关于半开区间,有一个严重的问题:你需要能够表示不在区间内的量。因此,例如,长度为 256 的字符串的索引类型至少需要是 short,即使每个有效索引都是一个字节。类似地,指向向量末端的半开区间描述符包含一个地址,该地址不包含在向量的存储中,并且很可能是一个指向不同对象的有效指针;这会使保守垃圾回收变得复杂,例如。(至少有一个保守垃圾回收器故意过度分配以弥补这个问题。)我并没有偏袒哪一方:两者都是有效的,都有优点,也有缺点。--RiciLake
就像从一开始计数一样,我认为闭合区间比半开区间更直观,至少当你没有谈论零长度区间的特殊情况时。当我提到“直观”时,我的意思是对于那些至少几年没有编程的人来说,它更自然。--PeterPrade
我没有统计过,但我认为大量的编程语言都是从零开始计数的……而且由于我学习的第一门语言是 C,从一开始计数对我来说很混乱——事实上,我写了很多从零开始的数组的糟糕的 Lua 代码——AdrianPerez
虽然从零开始计数有其优点,但我发现从一开始计数更自然,即使在编程时也是如此。对我来说,在 Lua 和 C 之间切换没有问题,因为过去我在 Visual Basic 和 C 之间切换了很多次——匿名
第一个是第 1 个,而不是第 0 个 --Kazimir Majorinc
“我正在评估 Lua 作为我应用程序中的嵌入式脚本语言,到目前为止,我看到的一切都非常积极,直到我意识到 Lua 从索引 1 开始计数。糟糕。糟糕。太糟糕了。糟糕到足以让我考虑把整个东西都扔掉。至于理由,即使不应该有任何必要:),长度为 N 的数组的索引是模 N 整数环的自然映射,它有很多不错的特性。例如,当你想循环访问你的数组时,你只需执行 index = (index+1) %N
。用从 1 开始的索引做同样的事情很痛苦。此外,它使得将 C 例程绑定到 Lua 非常痛苦。”
iterator+1
来访问表索引。--DanHollis?
lua_createtable()
API 调用,它允许你指定表的数组大小(精确地)。你也可以指定哈希大小,但那部分总是限制为 2 的幂。如果你创建了一个具有指定数组大小的表,只要你不强制表扩展,它的数组部分就会精确地是那个大小,所以如果你事先知道表的大小,它就会正常工作。绑定小原子对象的数组,比如 int
,在 5.1 中也变得稍微容易一些,因为有可能覆盖 #
运算符,但是为了让 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 <[email protected]>
[n,n)
,而且相邻区间也很好地加起来 -- [a,b)+[b,c)=[a,c)
-- 不会在两端过度加倍。得到的区间长度为 (b-a)+(c-b)=c-a
,并且任何或两个分量都可以是零长度。此外,可以考虑 `负区间' [m,n)
,其中 m>n
,并且区间的加法仍然有效,现在甚至可以从非零区间获得零长度区间。
现在,考虑数字 a
、b
和 x
。`x
在 a
和 b
之间' 的正确表达式,在 min(a,b)<=x<max(a,b)
(半开区间)的意义上是 (a<=x)=(x<b)
。如果是闭区间(...<=x<=...
),我们就不会有这么好的表达式。-- Boyko Bantchev
作为一种脚本语言,它的首要任务是易于脚本编写者使用。一个包含 5 个元素的数组,最后一个元素是数字 5,肯定比它在元素 4 处结束更容易向非专业脚本编写者解释?
我在我的系统中将许多客户端函数与接口连接起来,以将其工作原理暴露给 Lua。在许多情况下,基于零或一的问题甚至不适用。例如,许多使用字符串键(在这种情况下问题就消失了),或者它们不使用任何类型的数组。
如果你希望 Lua 成为一种通用的脚本语言,我当然不建议使用编译时选项,这样发布的一半 Lua 脚本基于零,一半基于 1。这样做会打开通往噩梦的大门。我甚至无法想象如果你想与 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,除非你在位置 1,在这种情况下,你需要减去 2。
是的,这是真的。但是,鉴于“事情已经做完了”,我建议需要“从零开始计数”方法的应用程序(出于数学或其他原因),只需定义自己的 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,最后一个元素是 -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}。
i==#t+1
(i==#t
基于0)被删除。我不理解它。它现在会引发错误。
其余部分应该很简单,包括一些负索引被支持为反转的情况,在这种情况下,它将主要像python一样工作。
我反对基于1的表的另一个理由是:它在与JSON(一种广泛使用的交换格式)进行转换时会导致问题和混淆。
--farter