-- original table
t = {
Steve = 20,
Mary = 32,
Tim = 15
}
--second table to help sort t{}
a = {}
for n in pairs(t) do
a[#a + 1] = n -- I do not completely understand this line.
-- I know [#a + 1] is the iterator, but
-- not a[#a + 1] and why equal that to "n"?
end
table.sort(a)
for _, n in ipairs(a) do -- I do not understand the point of the underscore
print (n)
end
-- another way to perform the table.sort function
function pairsByKeys (t,f)
local b = {}
for x in pairs (t) do
b[#b + 1] = x
end
table.sort(b, f)
local i = 0
return function ()
i = i + 1
return b[i], t[b[i]] -- I do not understand this line either?
end
end
我已经在特定代码后面的评论中提出了几个问题。我理解(大部分)为什么需要两个单独的表。一个拥有信息,另一个通过ipairs
对信息进行排序。我知道pairs
和ipairs
之间的区别。但我不清楚a[#a + 1] = n
。另外,我不理解代码for _, n in ipairs(a)
...,为什么是下划线?最后,我不清楚return b[i], t[b[i]]
行?
你能帮我理解table.sort好一点吗?这些是PiL的直接示例。我很感激帮助!
答案 0 :(得分:6)
首先,您应该了解Lua表的真正含义。从表面上看,它们显示为关联数组或映射(两个广为人知的CS术语),它们通常被实现为哈希表。它们以这种方式调用,因为它们将所谓的键(这是唯一的)与值相关联。它们可以被视为由键索引的键值对的集合,即以这样的方式实现,即使用(唯一)键可以非常快速地找到相应的值。
Lua表的两个主要特点,它们也使它们真正强大而灵活的数据结构(虽然起初有点难以理解),如下:
Lua表允许任何类型的值(nil
除外)作为键。在大多数语言中,键被限制为字符串,或者是必须事先声明的特定类型。在Lua中,表格可以随时随地保存任何类型的值。因此,这样定义的表在Lua中完全合法:
f = function(x) return x*x end
t = { 1, 2, 3 }
tbl = {
[t] = true, -- `t` is the key (and a table), `true` is the value
[f] = 12, -- `f` is the key (and a function), `12` is the value
[true] = f, -- `true` is the key (boolean), `f` now is used as value
[12] = f, -- `12` is the key (number), `f` again as the value
["yup"] = t, -- `"yup"` is the key (string), `t` now is used as value
}
正整数键具有特殊状态,因为它们用于模拟数组。 Lua没有正确的 array 概念。在Lua中,我们使用类似数组的表(a.k.a。序列,使用新的Lua 5.2术语)。很多时候,当你在Lua的上下文中看到术语 array 时,实际上编写器意味着类似于数组的表,我将在下面做同样的事情为简单起见,不会出现歧义。 Lua中的数组是什么?它是一个表,其正整数键从1
开始并以某个整数n
结束,即其正整数键仅为数字1,2,...,n(另一种说法是说正整数键形成集合{1,2,...,n})。该数字n
称为序列(数组)的长度,它是应用于数组时#
运算符返回的数字。如果表具有此属性,则称为序列,即可以称为数组。请注意,具有该属性的表仍然是以下数组:
1.23
); 0
或-12
)。"通用表"之间的区别并且数组不仅是术语的便利。在引擎盖下,Lua实现识别表是否确实是一个数组,并执行一些优化,允许Lua表在使用类似数组的表作为数组时具有高性能(例如,在C中表示)。实际上,Lua标准table
库假定提供给其函数的表(如table.sort
)确实是数组,并且只对具有正整数索引的条目进行操作。
考虑到所有这些,我们可以分析您发布的代码中的难点。
a = {}
for n in pairs(t) do
a[#a + 1] = n
end
这是一个通用的for循环的例子。 pairs
返回(除其他外)表迭代器函数(因此pairs
和ipairs
可以被称为迭代器生成函数或迭代器生成器)。 for
机制重复调用此迭代器函数,以迭代t
的所有键(和相应的值)。由于只有一个变量出现在for
中(即n
),因此在迭代过程中只会检索t
的键。
a[#a + 1] = n
只是将存储在n
中的密钥附加到表a
的快速方法,后者原来是一个数组,因为它是在迭代过程中逐步构建的仅从1开始的顺序正整数键。请记住,#a
是a
的当前长度(最初为0
,因为a
没有条目),因此{{ 1}}创建一个带有整数键a[#a+1]
的新条目,而不会中断#a + 1
的序列属性。
总结一下,a
循环只是收集数组for
中t
的所有键,以便使用a
对它们进行排序,然后打印出来:
table.sort
上一个是通用的另一个例子。在这种情况下,table.sort(a)
for _, n in ipairs(a) do
print (n)
end
返回的迭代器函数将在迭代期间(按此顺序)返回(正整数)键和ipairs
的值。由于我们只对打印值感兴趣(键将为a
,1
,...等,因为2
是一个数组),我们使用a
作为一个虚拟变量来获取(对我们无关的)密钥。我们可以使用另一个名称,但在Lua中使用(完全合法和正常)名称_
来完成此任务是惯用的。
_
的定义有点难以分析。它的目的是让一个迭代器生成器(pairsByKeys
)返回一个迭代器函数,它可以遍历一个表,保证迭代是根据特定的键顺序完成的(Lua pairsByKeys
不保证任何特定的迭代顺序)。它意味着像这样使用:
pairs
让我们分析一下这个定义。我们将看到它在一个函数中包含了我们已经分析过的代码的逻辑(加上一个增强)。
for k, v in pairsByKeys( t ) do
print( k, v )
end
首先要注意的是function pairsByKeys(t,f)
local b = {}
for x in pairs(t) do
b[#b + 1] = x
end
table.sort(b, f)
local i = 0
return function()
i = i + 1
return b[i], t[b[i]]
end
end
返回一个函数(迭代器),它实际上是一个带有三个upvalues的匿名闭包(pairsByKeys
,i
和t
)。这意味着返回的函数将能够在b
机制执行时引用这三个变量(此闭包是有状态迭代器的一个示例)。
在返回迭代器之前,for
" preprocesses"要迭代的表pairsByKeys
提取其密钥并对其进行排序,如上所述。因此,t
将按b
将t
放置的顺序保存table.sort(b,f)
的所有键。请注意,对table.sort
的此调用具有附加参数f
,这是一个比较器函数,可以在调用pairsByKeys
时指定。这允许根据不同的标准对键进行排序(这是"增强"我告诉过你)。
i
变量将保存刚刚迭代过的b
中的键的索引。因为在这个阶段没有发生迭代(尚未创建迭代器)。
现在让我们关注迭代器函数:
function()
i = i + 1
return b[i], t[b[i]]
end
每当for
机制调用它时,它将递增i
,然后它将获取b[i]
,这是要迭代的下一个键(它从{{ 1}}因为它b
包含有关其排序的信息),然后它再次使用b
从原始表{{1}中获取相应的值b[i]
},其中包含此信息。返回键和值,这两个值是(在每次迭代中)分配给上面示例的循环变量t[b[i]]
和t
的值,然后打印它们。
答案 1 :(得分:3)
--second table to help sort t{}
a = {}
for n in pairs(t) do
a[#a + 1] = n
end
[#a + 1]
不是迭代器 - 这是对表中特定元素的索引访问。 #a
是表格大小。通过向此添加+1,您将访问一个表中的最后一个元素。此操作会将n
附加到循环中表a
的末尾。
这与做:
相同 table.insert(a, n)
for _, n in ipairs(a) do -- I do not understand the point of the underscore
-- ...
这里的下划线仅用于表示未使用的变量。 _
是一个有效的lua变量,但它在lua中没有任何特殊含义。您可以将其命名为dummy
,但这不会产生任何影响。
这里需要假人,因为pairs
和ipairs
返回一个键后跟一个值。此代码想要“跳过”键并只使用该值。
-- ...
return function ()
i = i + 1
return b[i], t[b[i]] -- I do not understand this line either?
end
这是一个匿名闭包,在调用时会返回2个值。 b[i]
是表b
中的下一个元素。 t[b[i]]
使用t
作为密钥访问b[i]
中的值。
更明确的步骤如下:
return function ()
i = i + 1
local nextkey = b[i]
local nextvalue = t[nextkey]
return nextkey, nextvalue
end
答案 2 :(得分:1)
for n in pairs(t) do
a[#a + 1] = n
end
#
运算符返回表中元素的数量(实际上它只返回表中最高的数字索引,但在本例中它没有区别)。在此循环中,n
是从pairs(t)
返回的第一个值(值对),它是表中当前元素的键。因此,代码可以被视为对此进行评估:
a[1] = 'Steve'
a[2] = 'Mary'
a[3] = 'Tim'
for _, n in ipairs(a) do
这里的下划线是一个Lua约定,用于何时不需要ipairs
的索引值。它可以看作是Lua从函数返回多个值的能力的副作用。第一个值实际存储在名为_
的变量中,但程序员故意不使用它。