Lua中的table.sort:不明白这个代码?

时间:2013-09-27 21:06:02

标签: sorting lua lua-table

  -- 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对信息进行排序。我知道pairsipairs之间的区别。但我不清楚a[#a + 1] = n。另外,我不理解代码for _, n in ipairs(a) ...,为什么是下划线?最后,我不清楚return b[i], t[b[i]]行?

你能帮我理解table.sort好一点吗?这些是PiL的直接示例。我很感激帮助!

3 个答案:

答案 0 :(得分: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没有正确的 array 概念。在Lua中,我们使用类似数组的表(a.k.a。序列,使用新的Lua 5.2术语)。很多时候,当你在Lua的上下文中看到术语 array 时,实际上编写器意味着类似于数组的表,我将在下面做同样的事情为简单起见,不会出现歧义。 Lua中的数组是什么?它是一个表,其正整数键从1开始并以某个整数n结束,即其正整数键仅为数字1,2,...,n(另一种说法是说正整数键形成集合{1,2,...,n})。该数字n称为序列(数组)的长度,它是应用于数组时#运算符返回的数字。如果表具有此属性,则称为序列,即可以称为数组。请注意,具有该属性的表仍然是以下数组:

    • 它有其他非数字键(例如字符串或表键);
    • 它有其他非整数的数字键(例如1.23);
    • 它有其他非正数的整数键(例如0-12)。
  3. "通用表"之间的区别并且数组不仅是术语的便利。在引擎盖下,Lua实现识别表是否确实是一个数组,并执行一些优化,允许Lua表在使用类似数组的表作为数组时具有高性能(例如,在C中表示)。实际上,Lua标准table库假定提供给其函数的表(如table.sort)确实是数组,并且只对具有正整数索引的条目进行操作。


    考虑到所有这些,我们可以分析您发布的代码中的难点。

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

    这是一个通用的for循环的例子。 pairs返回(除其他外)表迭代器函数(因此pairsipairs可以被称为迭代器生成函数迭代器生成器)。 for机制重复调用此迭代器函数,以迭代t的所有键(和相应的值)。由于只有一个变量出现在for中(即n),因此在迭代过程中只会检索t的键。

    a[#a + 1] = n 
    

    只是将存储在n中的密钥附加到表a的快速方法,后者原来是一个数组,因为它是在迭代过程中逐步构建的仅从1开始的顺序正整数键。请记住,#aa的当前长度(最初为0,因为a没有条目),因此{{ 1}}创建一个带有整数键a[#a+1]的新条目,而不会中断#a + 1序列属性

    总结一下,a循环只是收集数组fort的所有键,以便使用a对它们进行排序,然后打印出来:

    table.sort

    上一个是通用的另一个例子。在这种情况下,table.sort(a) for _, n in ipairs(a) do print (n) end 返回的迭代器函数将在迭代期间(按此顺序)返回(正整数)键和ipairs的值。由于我们只对打印值感兴趣(键将为a1,...等,因为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的匿名闭包(pairsByKeysit )。这意味着返回的函数将能够在b机制执行时引用这三个变量(此闭包是有状态迭代器的一个示例)。

    在返回迭代器之前,for" preprocesses"要迭代的表pairsByKeys提取其密钥并对其进行排序,如上所述。因此,t将按bt放置的顺序保存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,但这不会产生任何影响。

这里需要假人,因为pairsipairs返回一个键后跟一个值。此代码想要“跳过”键并只使用该值。


  -- ...
    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)

问题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

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