1
  -- 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 的直接例子。感谢您的帮助!

4

3 回答 3

6

首先,您应该了解 Lua 表的真正含义。从表面上看,它们表现为关联数组映射(两个广为人知的 CS 术语),通常以哈希表的形式实现。之所以这样称呼它们,是因为它们将一个所谓的(唯一的)与一个相关联。它们可以被视为由键索引的键值对的集合,即以使用(唯一)键可以非常快速地找到相应值的方式实现。

Lua 表的两个主要特点,也使它们真正强大和灵活的数据结构(虽然一开始完全理解有点棘手),如下所示:

  1. 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
    }
    
  2. 正整数键具有特殊状态,因为它们用于模拟数组。Lua 没有正确的数组概念。在 Lua 中,我们使用类似数组的表(又名序列,使用新的 Lua 5.2 术语)。很多时候,当您在 Lua 的上下文中看到术语数组时,实际上 writer 的意思是类似数组的 table,为了简单起见,我将在下面这样做,以免产生歧义。Lua 中的数组是什么?它是一个表,其正整数键以某个整数开始1和结束n, 即其正整数键只有数字 1, 2, ..., n(另一种说法是正整数键形成集合 {1, 2,...,n})。该数字n称为序列(数组)的长度,它是#运算符在应用于数组时返回的数字。如果表具有此属性,则称为序列,即可以称为数组。请注意,具有该属性的表仍然是一个数组,如果:

    • 它有额外的非数字键(例如字符串或表格键);
    • 它有额外的非整数数字键(例如1.23);
    • 它有额外的非正整数键(例如0or -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到 table的一种快速方法a,它原来是一个数组,因为它是在迭代过程中逐步构建的,只有从 1 开始的顺序正整数键。记住这#a是的当前长度a(最初是0,因为a没有条目),因此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有点难以分析。它的目的是让一个迭代器生成器(pairsByKeys)返回一个迭代器函数,该函数可以迭代一个表,以保证迭代是根据特定的键顺序完成的(Luapairs不保证任何特定的迭代顺序)。它应该像这样使用:

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

首先要注意的是它pairsByKeys返回一个函数(迭代器),它实际上是一个具有三个上值( 和 )的匿名itb。这意味着返回的函数将能够在机器执行时引用这三个变量(此闭包是有状态迭代器for的示例)。

在返回迭代器之前,pairsByKeys“预处理”要迭代的表,t提取其键并对它们进行排序,正如我们在上面已经看到的那样。因此将按放置它们的顺序b保存所有键。请注意,此调用有一个附加参数,它是一个比较器函数,可以在调用时指定。这允许根据不同的标准对键进行排序(这是我告诉你的“增强”)。ttable.sort(b,f)table.sortfpairsByKeys

该变量将保存刚刚被迭代i的键的索引。b因为在这个阶段没有发生迭代(迭代器还没有创建)。

现在让我们关注迭代器函数:

function()
  i = i + 1
  return b[i], t[b[i]] 
end

每当for机器调用 this 时,它会递增i,然后它会 fetch b[i],这是要迭代的下一个键(它从它获取它,b因为它b保存有关它们的排序信息),然后它b[i]再次使用它来获取相应的值从包含此信息t[b[i]]的原始表中。t键和值都被返回,这两个值是(在每次迭代中)分配给循环变量kv上面示例的值,然后打印它们。

于 2013-09-28T11:35:34.283 回答
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改用它来命名它,它在这里不会有什么不同。

此处需要该虚拟对象,因为pairsipairs返回一个键,后跟一个值。此代码想要“跳过”键并仅使用该值。


  -- ...
    return function ()
      i = i + 1
      return b[i], t[b[i]]  -- I do not understand this line either?
    end

这是一个匿名闭包,它在被调用时返回 2 个值。b[i]是 table 中的下一个元素bt[b[i]]正在访问作为键t使用的值。b[i]

更明确的步骤如下所示:

    return function ()
      i = i + 1
      local nextkey = b[i]
      local nextvalue = t[nextkey]
      return nextkey, nextvalue
    end
于 2013-09-27T22:10:04.793 回答
1

问题 1

for n in pairs(t) do
    a[#a + 1] = n
end

运算符返回表中元素的#数量(实际上它只返回表中的最高数字索引,但在本例中没有区别)。在这个循环中,n 是从 中返回的(值对中的)第一个值pairs(t),它是表中当前元素的键。因此,代码可以被视为对此进行评估:

a[1] = 'Steve'
a[2] = 'Mary'
a[3] = 'Tim'

问题2

for _, n in ipairs(a) do

ipairs这里的下划线是当不需要索引值时的 Lua 约定。它可以被视为 Lua 从函数返回多个值的能力的副作用。第一个值实际上存储在一个名为的变量中,_但程序员故意不使用它。

于 2013-09-27T22:11:14.370 回答