を返します……最初の nil 要素に達するまで続きます。
イテレータ(iterator)はオブジェクトの一種であり、標準テンプレートライブラリのコンテナ内の一部または全部の要素を走査するために使用できます。各イテレータオブジェクトはコンテナ内の特定のアドレスを表します。
Lua においてイテレータはポインタ型をサポートする構造であり、集合のすべての要素を走査できます。
ジェネリックforイテレータ
ジェネリックforは内部にイテレータ関数を保存します。実際にはイテレータ関数、状態定数、制御変数の3つの値を保存します。
ジェネリックforイテレータは集合の key/value ペアを提供します。構文形式は以下のとおりです:
for k, v in pairs(t) do
print(k, v)
end
上記のコードでは、k, v は変数リストです。pairs(t) は式リストです。
以下の実例を参照してください:
実例
for key,value in ipairs(array)
do
print(key, value)
end
上記のコードを実行した出力結果は次のとおりです:
1 Google 2 サンプル
上記の実例では、Lua がデフォルトで提供するイテレータ関数 ipairs を使用しました。
次に、ジェネリックforの実行プロセスを見てみましょう:
- まず、初期化として、in の後の式の値を計算します。式はジェネリックforが必要とする3つの値、すなわちイテレータ関数、状態定数、制御変数を返す必要があります。複数値の代入と同様に、式が返す結果の数が3つ未満の場合は自動的に nil で補完され、余分な部分は無視されます。
- 次に、状態定数と制御変数を引数としてイテレータ関数を呼び出します(注意:for 構造にとって状態定数は役に立たず、初期化時にその値を取得してイテレータ関数に渡すだけです)。
- 次に、イテレータ関数が返した値を変数リストに代入します。
- 次に、返された最初の値が nil であればループを終了し、そうでなければループ本体を実行します。
- 次に、ステップ2に戻って再びイテレータ関数を呼び出します。
Lua では、イテレータを記述するために関数をよく使用します。この関数を呼び出すたびに集合の次の要素が返されます。Lua のイテレータには以下の2つのタイプがあります:
- ステートレスイテレータ
- マルチステートイテレータ
ステートレスイテレータ
ステートレスイテレータとは、いかなる状態も保持しないイテレータのことです。そのため、ループ内でステートレスイテレータを利用することで、クロージャを作成する余分なコストを回避できます。
各イテレーションにおいて、イテレータ関数は2つの変数(状態定数と制御変数)の値を引数として呼び出されます。ステートレスイテレータはこの2つの値だけを利用して次の要素を取得できます。
このようなステートレスイテレータの典型的で単純な例は ipairs です。これは配列のすべての要素を走査し、要素のインデックスは数値である必要があります。
以下の実例では、単純な関数を使用してイテレータを実装し、数値 n の平方を実現します:
実例
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 では次のように実装できます:
実例
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 の3つの値を取得します。次に Lua は iter(a,0) を呼び出して 1, a
マルチステートイテレータ
多くの場合、イテレータは単純な状態定数と制御変数ではなく、複数の状態情報を保存する必要があります。最も簡単な方法はクロージャを使用することです。もう1つの方法は、すべての状態情報を table 内にカプセル化し、その table をイテレータの状態定数として使用することです。この場合、すべての情報を table 内に格納できるため、イテレータ関数は通常2番目の引数を必要としません。
以下の実例では、独自のイテレータを作成しました:
実例
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 サンプル
上記の実例では、elementIterator 内でクロージャ関数を使用しており、集合のサイズを計算して各要素を出力しています。
その他の拡張