この初版は Lua 5.0 向けに書かれました。あとから出たバージョンでほとんど関連性は持っていますが、違う点もあります。
第 4 版は Lua 5.3 を対象にしており、Amazon やその他の書店で販売しています。
本を購入していただくと、Lua プロジェクトをサポートするのにも役立ちます。


7.4 – 複雑なステートを持つ反復処理

反復処理では、単一の不変状態と制御変数に収まるより多くのステートを保持する必要があることがよくあります。最も簡単な解決策は、クロージャーを使用することです。別の解決策は、必要なものをすべてテーブルに詰め込んで、そのテーブルを反復処理の不変状態として使用することです。テーブルを使用すると、反復処理では必要な量のデータをすべて保持できます。さらに、データを反復中に変更できます。ステートは常に同じテーブル(したがって不変)ですが、テーブルの内容は反復中に変化します。このような反復処理では、すべてのデータがステートにあるため、通常は汎用の for で提供される 2 番目の引数(反復処理変数)は破棄されます。

このテクニックの例として、現在の入力ファイルからすべての単語を走査する反復処理 allwords を書き換えます。今回は、linepos という 2 つのフィールドを含むテーブルを使用してステートを保持します。

反復処理を開始する関数は単純です。反復処理関数と初期ステートを返す必要があります。

    local iterator   -- to be defined later
    
    function allwords ()
      local state = {line = io.read(), pos = 1}
      return iterator, state
    end
iterator 関数は実際の処理を行います。
    function iterator (state)
      while state.line do        -- repeat while there are lines
        -- search for next word
        local s, e = string.find(state.line, "%w+", state.pos)
        if s then                -- found a word?
          -- update next position (after this word)
          state.pos = e + 1
          return string.sub(state.line, s, e)
        else    -- word not found
          state.line = io.read() -- try next line...
          state.pos = 1          -- ... from first position
        end
      end
      return nil                 -- no more lines: end loop
    end


できれば、状態をすべて for 変数に保持するステートレスな反復処理を書く必要があります。これらを使用すると、ループを開始するときに新しいオブジェクトを作成しません。反復処理がこのモデルに収まらない場合は、クロージャーを試す必要があります。さらにエレガントであることに加えて、通常、クロージャーはテーブルを使用する反復処理よりも効率的です。まず、クロージャーを作成する方がテーブルを作成するよりも安価です。2 番目に、アップバリューへのアクセスは、テーブルフィールドへのアクセスよりも高速です。後で、コルーチンを使用して反復処理を記述する別の方法を確認します。これは最も強力な解決策ですが、少しコストがかかります。