第一版是针对 Lua 5.0 编写的。虽然在很大程度上仍然适用于后续版本,但有一些差异。
第四版针对 Lua 5.3,可在 亚马逊 和其他书店购买。
购买本书,您还可以帮助支持 Lua 项目


5.1 – 多个结果

Lua 的一个非常规但非常方便的功能是函数可以返回多个结果。Lua 中的几个预定义函数返回多个值。一个示例是 string.find 函数,它在字符串中查找模式。它返回两个索引:模式匹配开始的字符索引和模式匹配结束的字符索引(如果找不到模式,则返回 nil)。多重赋值允许程序获取两个结果

    s, e = string.find("hello Lua users", "Lua")
    
    print(s, e)   -->  7      9

用 Lua 编写的函数也可以返回多个结果,方法是在 return 关键字后列出所有结果。例如,一个用于查找数组中最大元素的函数可以同时返回最大值及其位置

    function maximum (a)
      local mi = 1          -- maximum index
      local m = a[mi]       -- maximum value
      for i,val in ipairs(a) do
        if val > m then
          mi = i
          m = val
        end
      end
      return m, mi
    end
    
    print(maximum({8,10,23,12,5}))     --> 23   3

Lua 始终根据调用的情况调整函数返回的结果数。当我们以语句的形式调用函数时,Lua 会丢弃其所有结果。当我们以表达式的形式使用调用时,Lua 仅保留第一个结果。只有当调用是表达式列表中的最后一个(或唯一)表达式时,我们才能获得所有结果。这些列表出现在 Lua 中的四个结构中:多重赋值、函数调用的参数、表构造函数和 return 语句。为了说明所有这些用法,我们将在以下示例中假设以下定义

    function foo0 () end                  -- returns no results
    function foo1 () return 'a' end       -- returns 1 result
    function foo2 () return 'a','b' end   -- returns 2 results

在多重赋值中,函数调用作为最后一个(或唯一)表达式会生成与匹配变量所需的结果一样多的结果

    x,y = foo2()        -- x='a', y='b'
    x = foo2()          -- x='a', 'b' is discarded
    x,y,z = 10,foo2()   -- x=10, y='a', z='b'
如果函数没有结果,或者没有我们所需的结果那么多,Lua 会生成 nil
    x,y = foo0()      -- x=nil, y=nil
    x,y = foo1()      -- x='a', y=nil
    x,y,z = foo2()    -- x='a', y='b', z=nil
不是列表中最后一个元素的函数调用始终会产生一个结果
    x,y = foo2(), 20      -- x='a', y=20
    x,y = foo0(), 20, 30  -- x=nil, y=20, 30 is discarded

当函数调用是另一个调用的最后一个(或唯一)参数时,第一个调用的所有结果都作为参数。我们已经看到这种结构的示例,其中包含 print

    print(foo0())          -->
    print(foo1())          -->  a
    print(foo2())          -->  a   b
    print(foo2(), 1)       -->  a   1
    print(foo2() .. "x")   -->  ax         (see below)
当对 foo2 的调用出现在表达式中时,Lua 会将结果数调整为 1;因此,在最后一行中,只有 "a" 用于连接。

print 函数可以接收可变数量的参数。(在下一节中,我们将看到如何编写具有可变数量参数的函数。)如果我们编写 f(g()) 并且 f 具有固定数量的参数,Lua 会将 g 的结果数调整为 f 的参数数,如我们之前所见。

构造函数还收集调用中的所有结果,没有任何调整

    a = {foo0()}         -- a = {}  (an empty table)
    a = {foo1()}         -- a = {'a'}
    a = {foo2()}         -- a = {'a', 'b'}
与往常一样,这种行为仅在调用是列表中的最后一个调用时发生;否则,任何调用都只产生一个结果
    a = {foo0(), foo2(), 4}   -- a[1] = nil, a[2] = 'a', a[3] = 4

最后,像 return f() 这样的语句将返回 f 返回的所有值

    function foo (i)
      if i == 0 then return foo0()
      elseif i == 1 then return foo1()
      elseif i == 2 then return foo2()
      end
    end
    
    print(foo(1))     --> a
    print(foo(2))     --> a  b
    print(foo(0))     -- (no results)
    print(foo(3))     -- (no results)

你可以通过将调用括在额外的圆括号对中来强制调用返回一个结果

    print((foo0()))        --> nil
    print((foo1()))        --> a
    print((foo2()))        --> a
请注意,return 语句不需要在返回值周围加上圆括号,因此放在那里的任何圆括号对都算作额外的圆括号对。也就是说,像 return (f()) 这样的语句总是返回一个单一值,无论 f 返回多少个值。也许这是你想要的,也许不是。

具有多个返回值的特殊函数是 unpack。它接收一个数组,并从索引 1 开始将数组中的所有元素作为结果返回

    print(unpack{10,20,30})    --> 10   20   30
    a,b = unpack{10,20,30}     -- a=10, b=20, 30 is discarded

unpack 的一个重要用途是在通用调用机制中。通用调用机制允许你动态地调用任何函数,并带有任何参数。例如,在 ANSI C 中,没有办法做到这一点。你可以声明一个接收可变数量参数的函数(使用 stdarg.h),并且可以使用指向函数的指针来调用可变函数。但是,你不能使用可变数量的参数调用函数:你在 C 中编写的每个调用都有一个固定数量的参数,并且每个参数都有一个固定的类型。在 Lua 中,如果你想使用数组 a 中的可变参数调用可变函数 f,你只需编写

    f(unpack(a))
unpack 的调用将返回 a 中的所有值,这些值将成为 f 的参数。例如,如果我们执行
    f = string.find
    a = {"hello", "ll"}
那么调用 f(unpack(a)) 将返回 3 和 4,与静态调用 string.find("hello", "ll") 完全相同。

虽然预定义的 unpack 是用 C 编写的,但我们也可以使用递归在 Lua 中编写它

    function unpack (t, i)
      i = i or 1
      if t[i] ~= nil then
        return t[i], unpack(t, i + 1)
      end
    end
我们第一次调用它时,带有一个参数,i 获得 1。然后函数返回 t[1],后跟 unpack(t, 2) 的所有结果,而后者又返回 t[2],后跟 unpack(t, 3) 的所有结果,依此类推,直到最后一个非 nil 元素。