この初版は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. | ![]() |