この初版は Lua 5.0 用に書かれました。後続バージョンにも依然として関連性はありますが、いくつかの違いがあります。
第 4 版では Lua 5.3 を対象として おり、Amazon や他の書店で購入できます。
本の購入により、Lua プロジェクトのサポートにも協力することになります。


9.3 – イテレータとしての関数

ループのイテレータを、生産者コンシューマーのパターンのかなり具体的な例と見なすことができます。イテレータは、ループの本文によって消費される項目を生成します。したがって、イテレータの記述にコルーチンを使用するのは適切なようです。実際、コルーチンは、このタスクに強力なツールを提供します。ここでも、重要な機能は、呼び出し側と呼び出し先の関係を逆転できることです。この機能により、イテレータへの連続的な呼び出し間に状態を保持する方法を心配せずにイテレータを作成できます。

この種の使用を説明するために、指定された配列のすべての順列を走査するためのイテレータを作成します。そういったイテレータを直接記述するのは簡単なタスクではありませんが、それらのすべての順列を生成する再帰関数を作成するのは難しくありません。この考え方はシンプルです。すべての配列の要素を順番に最後の位置に配置し、残りの要素のすべての順列を再帰的に生成します。コードは次のとおりです

    function permgen (a, n)
      if n == 0 then
        printResult(a)
      else
        for i=1,n do
    
          -- put i-th element as the last one
          a[n], a[i] = a[i], a[n]
    
          -- generate all permutations of the other elements
          permgen(a, n - 1)
    
          -- restore i-th element
          a[n], a[i] = a[i], a[n]
    
        end
      end
    end
これが機能していることを確認するために、適切な printResult 関数を定義し、適切な引数を使用して permgen を呼び出す必要があります
    function printResult (a)
      for i,v in ipairs(a) do
        io.write(v, " ")
      end
      io.write("\n")
    end
    
    permgen ({1,2,3,4}, 4)

ジェネレーターの準備ができたら、イテレータに変換するのは自動的なタスクになります。まず、printResultyield に変更します

    function permgen (a, n)
      if n == 0 then
        coroutine.yield(a)
      else
      ...
次に、ジェネレーターがコルーチン内で実行されるように工場を定義し、イテレータ関数を生成します。イテレータは、次の順列を生成するためにコルーチンを再開するだけです
    function perm (a)
      local n = table.getn(a)
      local co = coroutine.create(function () permgen(a, n) end)
      return function ()   -- iterator
        local code, res = coroutine.resume(co)
        return res
      end
    end
その機能が整えば、for ステートメントを使って配列のすべての順列を反復処理することは簡単です
    for p in perm{"a", "b", "c"} do
      printResult(p)
    end
      --> b c a
      --> c b a
      --> c a b
      --> a c b
      --> b a c
      --> a b c

perm 関数は、対応するコルーチンの resume 呼び出しを関数内にまとめてパックする、Lua で一般的なパターンを使用します。このパターンは非常に一般的なため、Lua はこれに対応する特別な関数である coroutine.wrap を提供します。create と同様に、wrap は新しいコルーチンを作成します。 create とは異なり、wrap はコルーチン自体を返しません。代わりに、呼び出し時にコルーチンを再開する関数を返します。元の resume とは異なり、その関数は最初の結果としてエラーコードを返しません。代わりに、エラーが発生するとエラーを送出します。 wrap を使用すると、perm を次のように記述できます

    function perm (a)
      local n = table.getn(a)
      return coroutine.wrap(function () permgen(a, n) end)
    end

通常、coroutine.wrapcoroutine.create よりも使いやすくなります。コルーチンから必要な、つまり再開するための関数が得られます。ただし、柔軟性は低くなります。 wrap で作成されたコルーチンの状態を確認する方法はありません。さらに、エラーを確認することはできません。