この初版はLua 5.0用に書かれています。後続のバージョンでも大部分は関連性がありますが、いくつかの違いがあります。
第4版はLua 5.3を対象としており、Amazonおよびその他の書店で入手できます。
本書を購入することで、Luaプロジェクトの支援にもなります。


5.1 – 複数の戻り値

Luaの型破りだが非常に便利な機能として、関数が複数の戻り値を返すことができる点が挙げられます。Luaのいくつかの組み込み関数は複数の値を返します。例として、文字列内でパターンを検索するstring.find関数が挙げられます。この関数は2つのインデックスを返します。パターンの一致が始まる文字のインデックスと、一致が終了するインデックスです(パターンが見つからない場合は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の4つの構成要素(複数代入、関数呼び出しへの引数、テーブルコンストラクタ、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
リストの最後の要素ではない関数呼び出しは、常に1つの結果を生成します。
    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'}
常に、この動作は、呼び出しがリストの最後にある場合にのみ発生します。それ以外の場合は、どの呼び出しも正確に1つの結果を生成します。
    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)

余分な括弧のペアで囲むことで、呼び出しが正確に1つの結果を返すように強制できます。

    print((foo0()))        --> nil
    print((foo1()))        --> a
    print((foo2()))        --> a
return文は戻り値の周囲に括弧を必要としないため、そこに配置された括弧のペアは余分なペアとしてカウントされます。つまり、return (f())のようなステートメントは、fがいくつ値を返しても、常に1つの値を返します。これが必要な場合もあれば、そうでない場合もあります。

複数の戻り値を持つ特別な関数は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では、可変関数fを配列a内の可変引数で呼び出す場合は、次のように記述するだけです。

    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以外の要素まで続きます。