English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Lua イテレータ

イテレータ(iterator)は、標準テンプレートライブラリのコンテナ内の一部または全ての要素を巡回するためのオブジェクトです。各イテレータオブジェクトはコンテナ内の特定のアドレスを表します。

Luaでは、イテレータはポインタ型の構造体であり、集合の各要素を巡回できます。

ジェネリックforイテレータ

ジェネリックforは内部でイテレーション関数を保存しており、実際には3つの値を保存しています:イテレーション関数、状態定数、制御変数。

ジェネリックforイテレータはキーを提供する集合を提供します。/valueが正しい場合、以下の语法形式です:

for k, v in pairs(t) do
    print(k, v)
end

上記のコードでは、k、vが変数リストです;pairs(t)が式リストです。

以下の例を確認してください:

array = {"Google", "w3codebox"}
for key, value in ipairs(array) 
do
   print(key, value)
end

上記のコードの実行結果は以下の通りです:

1  Google
2  w3codebox

上記の例では、Luaが提供するデフォルトの巡回関数ipairsを使用しました。

以下に汎用forの実行プロセスを見てみましょう:

  • まず、初期化を行い、inの後の式の値を計算します。式は、汎用forが必要とする3つの値を返す必要があります:巡回関数、状態の定数、制御変数;多値割り当てと同様に、式が返す結果の数が3未満の場合は自動的にnilで補足され、余分な部分は無視されます。

  • 2つ目、状態の定数と制御変数を引数として巡回関数を呼び出します(注意:for構文では、状態の定数は使用されません。初期化時にその値を取得して巡回関数に渡すだけで済みます)。

  • 3つ目、巡回関数から返された値を変数リストに割り当てます。

  • 4つ目、最初の値がnilの場合はループが終了し、それ以外の場合はループ体を実行します。

  • 5つ目、ステップ2に戻って再び巡回関数を呼び出します

Luaでは、イテレータを関数を使用して表現することがよく行われます。関数を呼び出すたびに、集合の次の要素が返されます。Luaのイテレータには以下の2つのタイプがあります:

  • 無状態のイテレータ

  • 多状態のイテレータ

無状態のイテレータ

無状態のイテレータは、どのような状態も保持しないイテレータであり、そのため、ループ内で無状態のイテレータを使用することで、クロージャの作成に伴う追加のコストを避けることができます。

各巡回で、巡回関数は2つの変数(状態の定数と制御変数)の値を使用して呼び出されます。無状態のイテレータは、これら2つの値を利用して次の要素を取得できます。

この無状態のイテレータの典型的なシンプルな例はipairsで、配列の各要素を巡回します。

以下の例では、簡単な関数を使用してイテレータを実現し、数字nの平方を計算します:

function square(iteratorMaxCount, currentNumber)
   if currentNumber < iteratorMaxCount
   then
      currentNumber = currentNumber+1
   return currentNumber, currentNumber*currentNumber
   end
end
for i, n in square,3,0
do
   print(i, n)
end

上記の例の出力結果は以下の通りです:

1    1
2    4
3    9

迭代的状態には、巡回中に変更されない状態の定数(被巡回のテーブル)と現在のインデックスの下位(制御変数)が含まれます。ipairsと巡回関数は非常にシンプルで、Luaでは以下のように実現できます:

function iter(a, i)
    i = i + 1
    local v = a[i]
    if v then
       return i, v
    end
end
 
function ipairs(a)
    return iter, a, 0
end

Lua が ipairs(a) を呼び出してループを開始すると、三つの値を取得します:イテレータ関数 iter、状態定数 a、制御変数の初期値 0;その後 Lua が iter(a,0) を呼び出して 1, a[1](a[1=nil);二回目のイテレーション呼び出しは iter(a,1) 返します 2, a[2……最初の nil 要素まで。

多状態のイテレータ

多くの場合、イテレータは単なる状態定数や制御変数ではなく、複数の状態情報を保存する必要があります。最も簡単な方法はクロージャを使用することですが、もう一つの方法はすべての状態情報をテーブルに統合し、テーブルをイテレータの状態定数として使用することです。この場合、すべての情報をテーブル内に保存できるため、イテレータの関数には通常二つめの引数は必要ありません。

以下の例では、独自のイテレータを作成しています:

array = {"Google", "w3codebox"}
function elementIterator(collection)
   local index = 0
   local count = #collection
   -- クロージャ関数
   return function ()
      index = index + 1
      if index <= count
      then
         --  現在のイテレータの要素を返します
         return collection[index]
      end
   end
end
for element in elementIterator(array)
do
   print(element)
end

上記の例の出力結果は以下の通りです:

Google
w3codebox

上記の例では、elementIterator 内でクロージャ関数が使用されており、集合のサイズを計算し、各要素を出力する方法が示されています。