Luaでtableを走査するpairs()とipairs()の違いとは?使い分け・注意点・実例を徹底解説
Luaにおいて、tableは最も重要なデータ構造です。配列のようにも使えますし、辞書やオブジェクトのようにkey-value形式でデータを保持することもできます。そのため、Luaで実用的なコードを書くには、tableを正しく走査する方法を理解しておく必要があります。
Luaでは、tableを走査する代表的な方法としてpairs()とipairs()があります。どちらもfor文と一緒に使われますが、動作は同じではありません。pairs()はtable内のkey-valueを幅広く走査するために使われ、ipairs()は1から始まる連続した数値インデックスを順番に走査するために使われます。
この違いを理解しないまま使うと、「なぜ一部のデータが表示されないのか」「なぜ順番が変わるのか」「なぜ途中でループが止まるのか」といったバグにつながります。本記事では、Luaのpairs()とipairs()の違い、使い方、注意点、実務での選び方を初心者にも分かりやすく解説します。
1. pairs()とipairs()とは?
pairs()とipairs()は、Luaのtableを走査するための関数です。どちらもfor文と組み合わせて使い、table内の要素を順番に取り出します。ただし、取り出す対象と順番の考え方が異なります。
pairs()は、tableに含まれるすべてのkey-valueを走査します。keyが文字列でも数値でも対象になります。一方、ipairs()は、配列のように使われているtableを対象にし、1、2、3という連続した数値インデックスを順番に走査します。
| 関数 | 基本的な役割 |
|---|---|
pairs() | table内のすべてのkey-valueを走査する |
ipairs() | 1から始まる連続した数値インデックスを順番に走査する |
1.1 pairs()の基本的な役割
pairs()は、table内のkey-valueを広く走査するために使います。keyが文字列でも数値でも対象になります。そのため、設定データ、ユーザー情報、JSONをデコードしたデータ、辞書型データなどに向いています。
ただし、pairs()では走査順序が保証されません。コード上ではname、age、cityの順番で書いたとしても、出力順が同じになるとは限りません。順番が重要な処理には注意が必要です。
1.2 ipairs()の基本的な役割
ipairs()は、tableを配列として扱うときに使います。Luaの配列は一般的に1から始まるため、ipairs()は1, 2, 3...という順番で値を取り出します。
ただし、ipairs()は連続したインデックスだけを対象にします。途中にnilや存在しないインデックスがあると、そこで走査が止まります。そのため、欠番のあるtableには向いていません。
1.3 2つの関数が必要な理由
Luaのtableは、配列にも辞書にも使える柔軟な構造です。そのため、すべてのtableに同じ走査方法を使うと、意図しない動作が起こりやすくなります。配列として扱いたい場合はipairs()、key-value全体を扱いたい場合はpairs()を使い分ける必要があります。
2. pairs()でtableを走査する方法
pairs()は、table内のすべてのkey-valueを走査したいときに使います。keyが文字列でも数値でも走査対象になります。辞書型データや設定情報のように、key名に意味があるtableではpairs()がよく使われます。
基本構文は次の通りです。
for key, value in pairs(table_name) do
-- 処理
end
2.1 基本例
次の例では、ユーザー情報を持つtableをpairs()で走査しています。
local user = {
name = "Nam",
age = 25,
city = "Ha Noi"
}
for key, value in pairs(user) do
print(key, value)
end
出力例は次のようになります。
name Nam
age 25
city Ha Noi
ただし、出力順は環境や実行状況によって変わる可能性があります。pairs()は「すべてのkey-valueを取得する」用途には向いていますが、「書いた順番通りに取得する」用途には向いていません。
2.2 pairs()が向いているデータ
pairs()は、keyに意味があるtableに向いています。たとえば、設定情報、ユーザープロフィール、辞書型データ、JSONデータ、オブジェクト風のtableなどです。
local config = {
host = "localhost",
port = 8080,
debug = true
}
for key, value in pairs(config) do
print(key, value)
end
このようなtableでは、host、port、debugというkey自体に意味があります。そのため、数値インデックス順に処理するipairs()ではなく、すべてのkey-valueを取得できるpairs()が適しています。
2.3 pairs()の注意点
pairs()の最大の注意点は、順序が保証されないことです。tableに入れた順番で出力されるとは限らないため、順番が重要なリスト処理には不向きです。
| 注意点 | 説明 |
| 順序が保証されない | 書いた順番や数値順に出るとは限らない |
| すべてのkeyを対象にする | 文字列keyも数値keyも走査する |
| 配列専用ではない | リストを順番に処理したい場合はipairs()が読みやすい |
| デバッグ時に順番で混乱しやすい | 出力順が変わっても仕様上は問題ではない |
3. ipairs()でtableを走査する方法
ipairs()は、tableを配列として扱うときに使います。1から始まる連続した数値インデックスを順番に走査するため、リスト形式のデータに向いています。
基本構文は次の通りです。
for index, value in ipairs(table_name) do
-- 処理
end
3.1 基本例
次の例では、果物のリストをipairs()で走査しています。
local fruits = {
"Apple",
"Banana",
"Orange"
}
for index, value in ipairs(fruits) do
print(index, value)
end
出力結果は次のようになります。
1 Apple
2 Banana
3 Orange
このように、ipairs()は配列要素を1番目から順番に取り出します。リストを上から順に処理したい場合、ipairs()は非常に分かりやすい方法です。
3.2 ipairs()が向いているデータ
ipairs()は、データが配列として並んでいる場合に向いています。たとえば、アイテム一覧、メニュー一覧、ユーザー一覧、ログ一覧、ステージ一覧などです。
local enemies = {
"slime",
"goblin",
"dragon"
}
for i, enemy in ipairs(enemies) do
print(i, enemy)
end
このようなデータでは、順番に意味があります。1番目、2番目、3番目と順番に処理したい場合、ipairs()を使うと意図が明確になります。
3.3 ipairs()の注意点
ipairs()は、連続した数値インデックスだけを走査します。途中に存在しないインデックスがあると、そこで走査が止まります。
local numbers = {
[1] = 10,
[2] = 20,
[4] = 40
}
for i, value in ipairs(numbers) do
print(i, value)
end
出力結果は次のようになります。
1 10
2 20
[4] = 40は出力されません。理由は、[3]が存在しないため、ipairs()がそこで走査を終了するからです。この性質は、Lua初心者がよくつまずくポイントです。
4. pairs()とipairs()の違い
pairs()とipairs()の違いは、走査対象、順序、nilの扱い、向いているデータ構造にあります。pairs()はtable全体を幅広く走査する関数で、ipairs()は配列部分を順番に走査する関数です。
結論から言うと、辞書型・設定データ・key-value形式ならpairs()、配列・リスト・順番が必要なデータならipairs()を使うのが基本です。
| 比較項目 | pairs() | ipairs() |
| 文字列keyの走査 | できる | できない |
| 数値keyの走査 | できる | 連続インデックスのみ |
| 走査順 | 保証されない | 1から順番 |
| 配列との相性 | 使えるが順序保証なし | 非常に向いている |
| 辞書型との相性 | 非常に向いている | 向いていない |
| nilの扱い | table内の存在するkeyを走査 | 最初の欠番で停止 |
| 主な用途 | key-value、設定、辞書 | 配列、リスト、順序付きデータ |
4.1 走査対象の違い
pairs()は、tableに含まれるすべてのkey-valueを対象にします。keyが文字列でも数値でも処理できます。一方、ipairs()は、1から始まる連続した数値インデックスだけを対象にします。
つまり、language = "Programming"のような文字列keyは、pairs()では取得できますが、ipairs()では取得できません。
4.2 順序の違い
pairs()は走査順を保証しません。出力順に依存するコードを書くと、予期しない動作になる可能性があります。一方、ipairs()は1、2、3という順番で走査します。
順番が重要な処理、たとえばメニュー表示、ランキング、ステージ順、CSV行処理などでは、ipairs()が分かりやすいです。
4.3 nilによる停止条件の違い
ipairs()は、最初に存在しないインデックスに到達した時点で停止します。たとえば、1番、2番、4番に値がある場合、3番が存在しないため4番は処理されません。
pairs()は、存在するkey-valueを走査するため、欠番のある数値keyも取得できます。欠番があるtableでは、pairs()の方が安全な場合があります。
5. 実例で見るpairs()とipairs()の違い
実際のコードで比較すると、pairs()とipairs()の違いは非常に分かりやすくなります。次のように、配列要素と文字列keyが混在したtableを用意します。
local data = {
"Lua",
"Python",
language = "Programming"
}
このtableには、数値インデックスの要素として"Lua"と"Python"があり、文字列keyとしてlanguageがあります。
5.1 pairs()を使った場合
for key, value in pairs(data) do
print(key, value)
end
出力例は次のようになります。
1 Lua
2 Python
language Programming
pairs()は、数値keyも文字列keyも走査します。そのため、languageも出力されます。ただし、出力順は保証されません。
5.2 ipairs()を使った場合
for index, value in ipairs(data) do
print(index, value)
end
出力結果は次のようになります。
1 Lua
2 Python
ipairs()は、1から始まる連続した数値インデックスだけを走査します。そのため、文字列keyであるlanguageは無視されます。
5.3 どちらが正しいのか
どちらが正しいかは、tableをどのように扱いたいかによって変わります。配列として言語リストだけを取り出したいならipairs()が正しいです。table内のすべての情報を取り出したいならpairs()が正しいです。
| 目的 | 適した関数 |
| リスト要素だけを順番に処理したい | ipairs() |
| 文字列keyも含めて全体を処理したい | pairs() |
| 順番が必要 | ipairs() |
| key-value構造を扱う | pairs() |
6. ipairs()でよくあるミス
ipairs()でよくあるミスは、欠番のあるtableに使ってしまうことです。Luaでは、tableの数値インデックスが必ず連続しているとは限りません。途中にnilが入ると、ipairs()はそこで処理を止めます。
この仕様を理解していないと、「tableには値があるのにループで出てこない」というバグに見えます。しかし、これはipairs()の正常な動作です。
6.1 欠番のあるtableをipairs()で走査する
local scores = {
[1] = 95,
[2] = 88,
[5] = 100
}
for i, score in ipairs(scores) do
print(i, score)
end
出力結果は次の通りです。
1 95
2 88
[5] = 100は出力されません。[3]が存在しないため、ipairs()はそこで停止します。
6.2 この場合はpairs()を使う
欠番のあるtableをすべて走査したい場合は、pairs()を使います。
for key, value in pairs(scores) do
print(key, value)
end
この場合、1、2、5のkey-valueが走査対象になります。ただし、順番は保証されません。順番も必要な場合は、keyを別途ソートする必要があります。
6.3 欠番があるデータを順番に処理したい場合
欠番のある数値keyを順番に処理したい場合は、pairs()でkeyを集めてからソートする方法があります。
local scores = {
[1] = 95,
[2] = 88,
[5] = 100
}
local keys = {}
for key in pairs(scores) do
table.insert(keys, key)
end
table.sort(keys)
for _, key in ipairs(keys) do
print(key, scores[key])
end
この方法なら、欠番があっても数値keyの順番で処理できます。
7. pairs()でよくあるミス
pairs()でよくあるミスは、出力順に依存してしまうことです。pairs()はすべてのkey-valueを走査できますが、順番は保証されません。たまたま期待通りの順番で出力されたとしても、それに依存するコードは避けるべきです。
特に、UI表示、ランキング、メニュー、順番付きステップなどでは、pairs()を使うと意図しない並びになる可能性があります。
7.1 pairs()の順番に依存する
次のようなコードは、一見問題なく見えるかもしれません。
local menu = {
start = "Start Game",
options = "Options",
quit = "Quit"
}
for key, value in pairs(menu) do
print(value)
end
しかし、この出力順は保証されません。Start Game、Options、Quitの順番で表示されるとは限りません。
7.2 順番が必要なら配列を使う
順番が必要な場合は、配列として定義し、ipairs()で走査します。
local menu = {
"Start Game",
"Options",
"Quit"
}
for i, item in ipairs(menu) do
print(i, item)
end
この場合、1番目から順番に表示できます。
7.3 key-valueと順番を両立したい場合
key名と順番の両方が必要な場合は、配列の中にtableを入れる方法があります。
local menu = {
{ id = "start", label = "Start Game" },
{ id = "options", label = "Options" },
{ id = "quit", label = "Quit" }
}
for i, item in ipairs(menu) do
print(i, item.id, item.label)
end
この構造なら、順番を保ちながら各項目にkey情報を持たせることができます。
8. 実務での使い分け
実務では、tableの形を見てpairs()とipairs()を選びます。tableが配列として設計されているならipairs()、辞書や設定データとして設計されているならpairs()を使うのが基本です。
重要なのは、tableを作る段階で「これは配列なのか」「これはkey-valueデータなのか」を明確にすることです。構造が曖昧なtableは、後からループ処理で混乱しやすくなります。
8.1 ipairs()を使うべき場面
ipairs()は、データが1から連続した配列で、順番に意味がある場合に使います。たとえば、アイテム一覧、ログ一覧、ステージ一覧、ランキング、入力順データなどです。
local stages = {
"tutorial",
"forest",
"castle",
"final_boss"
}
for i, stage in ipairs(stages) do
print("Stage " .. i .. ": " .. stage)
end
このようなケースでは、順番が明確なのでipairs()が適しています。
8.2 pairs()を使うべき場面
pairs()は、keyに意味があるデータを扱う場合に使います。設定情報、ユーザーデータ、辞書、マップ、JSONデータなどです。
local user = {
id = 101,
name = "Nam",
role = "admin"
}
for key, value in pairs(user) do
print(key, value)
end
このようなデータでは、順番よりもkey-valueをすべて取得することが重要です。
8.3 判断基準のまとめ
| tableの種類 | 例 | 推奨 |
| 配列 | { "A", "B", "C" } | ipairs() |
| 辞書 | { name = "Nam", age = 25 } | pairs() |
| 設定データ | { host = "...", port = 8080 } | pairs() |
| JSONオブジェクト | { user = "...", active = true } | pairs() |
| 欠番のある数値key | { [1]="A", [3]="C" } | pairs() |
| 順番付きオブジェクト一覧 | { {id=1}, {id=2} } | ipairs() |
9. pairs()とipairs()のベストプラクティス
pairs()とipairs()を安全に使うには、tableの設計を明確にし、順番が必要かどうかを意識することが大切です。特に、配列と辞書を混ぜたtableは便利ですが、走査時に意図が分かりにくくなることがあります。
実務では、配列として扱うtableは配列だけにし、辞書として扱うtableはkey-valueだけにする方が保守しやすいです。どうしても混在させる場合は、どの関数で走査するかを明確に決めておきましょう。
9.1 tableの役割を明確にする
tableを作るときは、配列なのか辞書なのかを明確にします。次のように混在させると、pairs()とipairs()のどちらを使うべきか分かりにくくなります。
local data = {
"Lua",
"Python",
language = "Programming"
}
この構造が必要な場合もありますが、初心者のうちは避けた方が安全です。配列部分とメタ情報を分けると分かりやすくなります。
local data = {
languages = { "Lua", "Python" },
category = "Programming"
}
9.2 順番が必要なら配列にする
順番が必要なデータは、配列として定義します。pairs()で偶然順番通りに見えても、その順番に依存しないようにしましょう。
local tasks = {
"install Lua",
"install LuaSocket",
"write TCP client",
"write TCP server"
}
for i, task in ipairs(tasks) do
print(i, task)
end
このように書けば、タスクの順番が明確になります。
9.3 全要素が必要ならpairs()を使う
keyの種類を問わず、table内の全要素を取得したい場合はpairs()を使います。特に設定データやデバッグ出力では便利です。
local settings = {
width = 1280,
height = 720,
fullscreen = false,
volume = 80
}
for key, value in pairs(settings) do
print(key, value)
end
10. よくある質問
pairs()とipairs()はシンプルに見えますが、実際にはLuaのtable仕様と深く関係しています。ここでは、初心者がよく疑問に感じるポイントを整理します。
10.1 pairs()は配列にも使えるのか?
pairs()は配列にも使えます。ただし、順番が保証されないため、配列を順番に処理したい場合はipairs()の方が適しています。デバッグ目的で全要素を見るだけならpairs()でも問題ありません。
10.2 ipairs()は文字列keyを処理できるのか?
ipairs()は文字列keyを処理しません。1から始まる連続した数値インデックスだけを処理します。文字列keyを含むtableを走査したい場合はpairs()を使います。
10.3 ipairs()が途中で止まるのはなぜか?
ipairs()は、最初に存在しないインデックスに到達した時点で停止するためです。たとえば、[1]と[2]が存在し、[3]が存在しない場合、[4]以降に値があっても処理されません。
10.4 順番付きの辞書を作りたい場合は?
Luaの通常のtableで辞書の順番を保証したい場合は、keyの配列を別に持つ方法がよく使われます。
local order = { "host", "port", "debug" }
local config = {
host = "localhost",
port = 8080,
debug = true
}
for _, key in ipairs(order) do
print(key, config[key])
end
この方法なら、key-value形式を保ちながら表示順を制御できます。
11. まとめ表:pairs()とipairs()の選び方
pairs()とipairs()の選び方は、tableの形と目的で決まります。迷った場合は、「順番が必要か」「文字列keyがあるか」「欠番があるか」を確認すると判断しやすくなります。
| 判断ポイント | 使うべき関数 |
| 1から順番に並んだ配列を処理したい | ipairs() |
| 文字列keyを含むtableを処理したい | pairs() |
| 設定データを処理したい | pairs() |
| JSONオブジェクトを処理したい | pairs() |
| 順番付きリストを処理したい | ipairs() |
| 欠番のある数値keyを処理したい | pairs() |
| 全key-valueを確認したい | pairs() |
| 表示順を固定したい | ipairs()またはkey配列を使う |
おわりに
pairs()とipairs()は、Luaでtableを走査するための基本的な関数です。どちらも重要ですが、目的は異なります。pairs()はtable内のすべてのkey-valueを走査するために使い、ipairs()は1から始まる連続した配列要素を順番に走査するために使います。
pairs()は、設定データ、辞書型データ、JSONオブジェクト、key-value形式のtableに向いています。ただし、走査順は保証されないため、順番が重要な処理には注意が必要です。一方、ipairs()は、配列やリストのような順序付きデータに向いています。ただし、途中に欠番やnilがあるとそこで走査が止まります。
実務では、tableを作る段階で「配列として使うのか」「辞書として使うのか」を明確にすることが重要です。順番が必要ならipairs()、すべてのkey-valueが必要ならpairs()を使うという基本を押さえておけば、Luaのtable操作で起こりやすいバグを避けやすくなります。
Luaのtableは非常に柔軟ですが、その柔軟さゆえに設計が曖昧になることもあります。pairs()とipairs()の違いを正しく理解し、データ構造に合った走査方法を選ぶことで、読みやすく保守しやすいLuaコードを書けるようになります。
EN
JP
KR