この初版はLua 5.0向けに執筆されました。以降のバージョンでも大部分の関連性は残っていますが、相違点もいくつかあります。
第4版はLua 5.3を対象とし、Amazonやその他の書店で購入できます。
この本を購入することで、Luaプロジェクトを支援することもできます。
![]() |
Luaでプログラミングする | ![]() |
| 1部 言語 7章 イテレータと汎用for |
イテレータとは、コレクションの要素を反復処理できる構造です。Luaでは、通常、イテレータは関数で表されます。その関数を呼び出すたびに、コレクションの「次の」要素が返されます。
どのイテレータも、その状態を連続する呼び出し間で維持する必要があります。これによって、どこにいるのか、そこからどのように処理を進めればよいかを認識できます。クロージャーは、このタスクに優れたメカニズムを提供します。クロージャーは、囲む関数内の1つまたは複数のローカル変数にアクセスする関数であることを思い出してください。これらの変数は連続するクロージャ呼び出し間でもその値を保持するため、クロージャはトラバーサルにおいてどこにいるのかを記憶できます。もちろん、新しいクロージャを作成するには、その外部ローカル変数も作成する必要があります。したがって、クロージャの作成には通常、2つの関数が関係します。クロージャ自体と、クロージャを作成するファクトリ(関数)です。
単純な例として、リストの単純なイテレータを作成してみましょう。ipairsとは異なり、このイテレータは各要素のインデックスではなく、値のみを返します。
function list_iter (t)
local i = 0
local n = table.getn(t)
return function ()
i = i + 1
if i <= n then return t[i] end
end
end
この例では、list_iterがファクトリです。それを呼び出すたびに、新しいクロージャ(イテレータそのもの)が作成されます。このクロージャは、その外部変数(t、i、n)内に状態を格納するため、呼び出すたびにリストtから次の値を返します。リストに値がなくなると、イテレータはnilを返します。そのようなイテレータはwhileと一緒に使用できます。
t = {10, 20, 30}
iter = list_iter(t) -- creates the iterator
while true do
local element = iter() -- calls the iterator
if element == nil then break end
print(element)
end
ただし、汎用forを使用する方が簡単です。結局のところ、それはそのような反復処理のために設計されたのですから。 t = {10, 20, 30}
for element in list_iter(t) do
print(element)
end
汎用forは、反復処理ループからすべての簿記を行います。イテレータファクトリを呼び出し、イテレータ関数を内部的に保持するため、iter変数は必要ありません。新しい反復処理のたびにイテレータを呼び出し、イテレータがnilを返すとループを停止します。(後で、汎用forは実際にはそれ以上のことを行うことがわかります。)より高度な例として、現在の入力ファイルからすべての単語をトラバースするイテレータを作成します。このトラバーサルを行うには、2つの値を保持する必要があります。現在の行と、その行内の位置です。このデータがあれば、いつでも次の単語を生成できます。それを保持するために、2つの外部ローカル変数lineとposを使用します。
function allwords ()
local line = io.read() -- current line
local pos = 1 -- current position in the line
return function () -- iterator function
while line do -- repeat while there are lines
local s, e = string.find(line, "%w+", pos)
if s then -- found a word?
pos = e + 1 -- next position is after this word
return string.sub(line, s, e) -- return the word
else
line = io.read() -- word not found; try next line
pos = 1 -- restart from first position
end
end
return nil -- no more lines: end of traversal
end
end
イテレータ関数の主要部分はstring.findを呼び出すことです。この呼び出しは、現在の行内の、現在の位置から始まる単語を検索します。パターン「%w+」を使用して「単語」を記述し、1つ以上の英数字に一致します。単語が見つかると、関数はその単語の後の最初の文字に現在位置を更新し、その単語を返します。(string.sub呼び出しは、指定された位置の間のlineからサブ文字列を抽出します)。それ以外の場合、イテレータは新しい行を読み取って検索を繰り返します。行がなくなると、反復処理の終了を知らせるためにnilを返します。その複雑さにもかかわらず、allwordsの使い方は簡単です。
for word in allwords() do
print(word)
end
これはイテレータに共通する状況です。書くのは難しいかもしれませんが、使うのは簡単です。大きな問題ではありません。多くの場合、Luaでプログラミングするエンドユーザーはイテレータを定義するのではなく、アプリケーションによって提供されるイテレータのみを使用しています。| Copyright © 2003–2004 Roberto Ierusalimschy. All rights reserved. | ![]() |