Lua/標準ライブラリー

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Lua

Luaは「電池を用意しない( "not be provided with batteries" )」と言われる言語です。つまり、そのライブラリーは、あることをするために必要な最小限のものに留められているのです。Luaは、より具体的なタスクを実行するために使用できるライブラリーを作成するために、そのコミュニティを頼りにしています。Luaリファレンスマニュアルは全てのライブラリーのドキュメントを提供しています[1] なので、ここでは簡単に説明するにとどめます。基本ライブラリーとパッケージライブラリーを除くすべてのライブラリーは、その関数と値をテーブルのフィールドとして提供します。

基本ライブラリー[編集]

基本ライブラリーは、Luaのコアとなる機能を提供します。その関数や値はすべてグローバル環境で直接利用でき、デフォルトでグローバル環境で直接利用できる関数や値はすべて基本ライブラリーの一部となっています。

アサーション[編集]

アサーション(assertion)とは、開発者が真であると仮定した条件です。プログラムの中で、実行中の特定の瞬間に特定の条件が真であることを保証するために使用されます。アサーションは、プログラムが正しく動作することを確認するためのユニットテストで使用されますが、プログラムコードの中でも使用されます。この場合、アサーションが偽の場合にプログラムが失敗するのは、プログラムが置かれた環境が正しいことを確認するため、あるいはプログラムコードに誤りがないことを確認し、期待通りのことが起こらない場合にコードのバグを見つけやすくするために、適切なエラーメッセージを発生させるため、などがあります。Luaでは、アサーションはassert関数で行います。assert関数は、条件とメッセージ(デフォルトは"assertion failed!")をパラメータとして受け取ります。条件が false と評価された場合、assert はメッセージとともにエラーをスローします。true と評価されると、assert はそのすべての引数を返します。

ガベージコレクション[編集]

ガーベッジコレクション(Garbage collection)は、Luaやその他多くの言語で実装されている自動メモリー管理の一形態です。プログラムは、変数にデータを格納する必要がある場合、オペレーティングシステムに、コンピューターのメモリー内に変数の値を格納するための領域を確保するよう要求します。そして、その領域が不要になったとき(一般的には変数がスコープ外になったため)、他のプログラムがその領域を使えるように、オペレーティングシステムに領域の割当てを解除するように指示します。Luaでは、実際の処理はもっと複雑ですが、基本的な考え方は同じです。プログラムは、変数の値が不要になったらオペレーティングシステムに伝えなければなりません。低レベル言語では、アロケーションは言語が処理しますが、デアロケーションは言語が処理しません。値を参照していた変数がスコープ外に落ちたり削除されたりしても、他の変数やスクリプトのフィールドがまだその値を参照しているかもしれず、デアロケーションを行うと問題が発生するためです。高レベルの言語では、デアロケーションは様々な自動メモリー管理システムによって処理されることがあります。例えば、Luaが採用しているシステムであるガベージコレクションがそうです。ガベージコレクションは、Luaが割当てたすべての値の中から、どこにも参照されていない値を定期的に探し出します。ガベージコレクションは、プログラムが(参照がないため)もうアクセスできない値を収集し、これらの値が安全に割当て解除されることを知っているので、その値を割当て解除します。これはすべて透過的かつ自動的に行われるため、プログラマは通常何もする必要がありません。しかし、時には、開発者がガベージコレクターに指示を出したい場合もあるでしょう。

弱い参照[編集]

弱い参照(Weak references)とは、ガベージコレクターに無視される参照です。これらの参照は、開発者がmodeメタメソッドを使ってガベージコレクターに指示します。テーブルのmodeメタメソッドは文字列でなければなりません。この文字列が文字 "k" を含んでいる場合、テーブルのフィールドのキーはすべて弱く、文字 "v" を含んでいる場合、テーブルのフィールドの値はすべて弱くなります。オブジェクトの配列が弱い値を持つ場合、ガベージコレクターはその配列で参照されるオブジェクトであっても、その配列と他の弱い参照でしか参照されない限り、これらのオブジェクトを回収します。この動作は便利なこともありますが、ほとんど使われません。

ガベージコレクターの操作[編集]

ガベージコレクターの操作は、基本ライブラリーの一部であり、ガベージコレクターへのインターフェイスとして機能するcollectgarbage関数で行うことができます。この関数の最初の引数は、ガベージコレクターにどのようなアクションを実行すべきかを示す文字列です。collectgarbage関数は、ガベージコレクターの停止、収集サイクルの手動実行、Luaが使用しているメモリーのカウントに使用することができます。

コルーチン[編集]

コルーチンは、サブルーチンを一般化し、特定の場所で実行を中断・再開するための複数のエントリポイントを可能にするコンピュータープログラムのコンポーネントです。

コルーチンは、協調タスク、例外処理、イベントループ、イテレータ、無限リスト、パイプなど、より身近なプログラムコンポーネントの実装に適しています。

—Wikipedia, Coroutine

コルーチン(Coroutines)とは、Luaのコルーチンライブラリーで作成・操作できるコンポーネントで、コルーチンを自身の内部から降伏させる関数や、自身の外部から再開させる関数を呼出すことで、特定の場所で関数の実行を降伏・再開させることができるようになるものです。

例:
  1. メインスレッドの関数が coroutine.create で関数からコルーチンを生成し、coroutine.resume で再開し、そこに数値 3 が渡されます。
  2. コルーチン内の関数が実行され、coroutine.resumeに引数として渡された数値を取得します。
  3. この関数は実行のある時点で、受け取った引数(3)と2の和を引数として coroutine.yield を呼出す(したがって、3+2=5)。
  4. coroutine.resume の呼出しは、coroutine.yield に渡されたので5を返し、再び実行中のメインスレッドはその数字を変数に格納します。メインスレッドは、いくつかのコードを実行した後、再びコルーチンを再開し、 coroutine.resumecoroutine.resume の呼出しから受け取った値の倍を渡す(つまり、5×2=10 を渡す)。
  5. コルーチンは coroutine.resume に渡された値を coroutine.yield の呼出しの結果として取得し、さらにいくつかのコードを実行した後、終了します。coroutine.yield の呼出しの結果と、最初にパラメータとして与えられた値との差(つまり、10-3=7)を返す。
  6. メインスレッドは coroutine.resume の呼出しの結果としてコルーチンから返された値を取得し、次に進みます。

この例をコードにすると、次のようになります。

local co = coroutine.create(function(initial_value)
	local value_obtained = coroutine.yield(initial_value + 2) -- 3+2=5
	return value_obtained - initial_value -- 10-3=7
end)

local _, initial_result = coroutine.resume(co, 3) -- initial_result: 5
local _, final_result = coroutine.resume(co, initial_result * 2) -- 5*2=10
print(final_result) --> 7

coroutine.createは関数からコルーチンを作成し、コルーチンは "thread "型である。コルーチンは、エラーが発生したとき、または、実行するものがなくなったとき(この場合、実行を終了した)、死んだと言われる。コルーチンが死ぬと、再開することはできません。coroutine.resume 関数は、コルーチンの実行が成功した場合、返されたすべての値とともに、コルーチンが終了した場合は true を返し、終了しなかった場合は coroutine.yield に渡されます。coroutine.resume は実行中のコルーチンを返し、そのコルーチンがメインスレッドの場合は true、そうでない場合は false を返します。

coroutine.status 関数は、コルーチンの状態を文字列で返す。

"running"
コルーチンが実行されている場合、つまり、coroutine.statusを呼出したコルーチンである必要があります
"suspended"
コルーチンがyieldの呼出しで一時停止されている場合、またはコルーチンがまだ実行を開始していない場合
"normal"
コルーチンがアクティブであるが実行されていない場合、つまり別のコルーチンを再開した場合
"dead"
コルーチンの実行が終了したか、エラーが発生した場合

coroutine.wrap関数は、それが呼ばれるたびにコルーチンを再開する関数を返します。この関数に与えられた追加引数は coroutine.resume の追加引数として動作し、コルーチンが返す値または coroutine.yield に渡される値は、この関数の呼出しによって返されることになります。coroutine.wrap 関数は、coroutine.resume とは異なり、protected mode でコルーチンを呼出さず、エラーを伝搬します。

コルーチンには多くの使用例がありますが、それらについて説明することは本書の範囲外です。

文字列マッチング[編集]

文字列を操作する際、あるパターンに従った部分文字列を検索できると便利なことがよくあります。Luaには文字列操作のためのライブラリーがあり、これを用いて文字列を検索するための関数と、関数が検索できるパターンを表現するための記法が用意されています。Luaが提供する表記法は、プログラミングの世界でほとんどの言語やツールが使用しているパターン表記法である正規表現に非常によく似ています。しかし、より限定的であり、構文も若干異なっています。

文字列ライブラリーのfind関数は、文字列の中からあるパターンに最初にマッチするも のを探す。文字列中にパターンが見つかった場合、そのパターンが始まる位置と終わる位置のインデックス(文字列中の文字の位置を表す整数で、最初の文字が1であることから始まる)を返します。もしそのパターンが見つからなければ、何も返しません。最初のパラメータは文字列、2番目はパターン、3番目はfind関数が検索を開始すべき文字位置を示す整数です。最後に、find関数の4番目の引数にtrueを指定すると、パターンによらない単純なマッチングを行うことができます。この場合、最初の文字列の中に与えられた2番目の文字列が含まれているかどうかを検索することになります。第3引数は、単純一致を実行するときに与えなければなりません。このサンプルコードでは、文中に "lazy" という単語があるかどうかを検索し、その単語の出現箇所の開始位置と終了位置を表示しています。

local start_position, end_position = string.find("The quick brown fox jumps over the lazy dog.", "lazy", 1, true)
print("The word \"lazy\" was found starting at position " .. start_position .. " and ending at position " .. end_position .. ".")

このコードは、The word "lazy" was found starting at position 36 and ending at position 39. をプリントします。 これは次のように等価である

local sentence = "The quick brown fox jumps over the lazy dog."
local start_position, end_position = sentence:find("lazy", 1, true)
print("The word \"lazy\" was found starting at position " .. start_position .. " and ending at position " .. end_position .. ".")

This works because the index metamethod of strings is set to the table containing the functions of the string library, making it possible to replace string.a(b, ...) by b:a(...).

これは、文字列のインデックスメタメソッドに文字列ライブラリーの関数を含むテーブルが設定されており、string.a(b, ...)b:a(...) で置換えることができるためです。

文字列ライブラリーの関数のうち、文字の位置を示す添字を受け付ける関数や添字を返す関数は、最初の文字を1の位置にあると見なします。負の数を受け取った場合は、文字列の末尾から逆算して、最後の文字が -1 の位置にあるものとして解釈されます。

パターンは、ある文字列がマッチするかしないかのパターンを示すために、ある記法に従った文字列です。この目的のために、パターンには、文字の集合を表す組み合わせである文字クラスが含まれます。

パターンの記法
文字の組合せ 概要
. すべてのキャラクター
%a 文字 (大文字と小文字)
%c 制御キャラクター
%d 数字
%g 印字可能な文字(スペース文字を除く)
%l 小文字
%p 句読点記号
%s 空白キャラクター
%u 大文字
%w 英数字 (数字と文字)
%x 16進数字

特殊でない文字はすべて自分自身を表し、特殊文字(英数字でないすべての文字)はパーセント記号を前につけることでエスケープすることができます。文字クラスは、セットにすることによって、より大きな文字クラスを作ることができます。集合は角括弧で囲まれた文字クラスとして表記されます (例: [%xp] はすべての16進文字に文字 "p" を加えた集合です)。文字の範囲は、昇順に、範囲の終わりの文字をハイフンで区切ることによって表記することができます(例: [0-9%s] は、0から9までのすべての数字と空白文字を表します)。キャレット("^")文字が集合の先頭(開始角括弧の直後)に置かれた場合、集合は、キャレットが集合の先頭に置かれなかった場合に含まれるであろう文字を除くすべての文字を含むことになります。

パーセント記号の前にある文字で表されるすべてのクラスの補集合は、パーセント記号の後に対応する大文字を続けて表記することができます(つまり、%Sはスペース文字を除くすべての文字を表しています)。

パターンは、文字列がパターンにマッチするために、どのようなシーケンスがパターン内にあるべきかを表すパターンアイテムのシーケンスです。この場合、クラス内の文字の0回以上の繰り返しにマッチします(これらの繰り返し項目は、常に可能な限り長いシーケンスにマッチします)。この場合、クラス内の文字の1つ以上の繰り返しにマッチします(これらの繰り返し項目も、可能な限り最長のシーケンスに常にマッチします)、マイナス("-")文字が続く文字クラス、この場合、クラス内の文字の0つ以上の繰り返しにマッチしますが、最短のシーケンスにマッチします、質問マークが続く文字クラス、この場合、クラス内の文字の1つまたは全く出現しないものにマッチします。

前に捕捉した部分文字列と等価な部分文字列にマッチさせることができます。 %1は最初に捕捉した部分文字列にマッチし、%2は2番目、そして%9まで続きます。キャプチャについては後述します。リファレンスマニュアルで説明されているように、パターンには他に2つの機能性があります。

%bxy この項目は、x で始まり y で終わる、x と y が釣り合っている文字列にマッチします。これは、文字列を左から右に読み、x を +1、y を -1 と数えると、終了する y はカウントが 0 になる最初の y であることを意味します。 たとえば、項目 %b() はバランスのとれた括弧を持つ式にマッチします。

%f[set] この項目は、次の文字が集合に属し、前の文字が集合に属さないような任意の位置で、空の文字列にマッチします。集合setは,前述したように解釈される.主語の先頭と末尾は'˶'字のように扱われます。

—Lua authors, Lua 5.2 Reference Manual

パターンは、パターン項目のシーケンスであり、オプションとして、その前に、パターンが文字列の先頭にのみマッチすることを示すカレットが付き、オプションとして、その後に、パターンが文字列の末尾にのみマッチすることを示すドル記号が付きます。これらの記号は、文字列の先頭または末尾でマッチをアンカーすることを意味します。この2つの文字は、パターンの先頭または末尾にある場合にのみ特別な意味を持ちます。

サブパターンは、キャプチャを示すために、パターン内の括弧で囲むことができます。マッチが成功した場合、マッチが捕捉した文字列の部分文字列は、将来 gmatch が返す時などに使えるように保存されます。これらの文字列には、常に左括弧の位置から始まる番号が振られます。2つの空白の括弧は空のキャプチャを表し、これは現在の文字列の位置をキャプチャします(これは数字であり、文字列の一部ではありません)。

gmatch関数は、文字列中のパターンの出現箇所を繰り返し検索するのに使われます。find関数とは異なり、検索を開始する最初の位置を指定したり、単純なマッチングを行ったりすることはできません。gmatch関数はイテレータを返し、それが呼ばれたとき、文字列中の与えられたパターンから次のキャプチャを返します。パターンに指定されたキャプチャがない場合は、代わりにマッチ全体が返されます。次の例は、文中の単語を反復して一つずつ表示する方法を示しています。

local sentence = "The quick brown fox jumps over the lazy dog."
for word in sentence:gmatch('%a+') do
	print(word)
end

この例では、イテレータが返す唯一の値であるwordによって、全体のマッチングが与えられます。

この例では、イテレータが返す唯一の値であるwordによって、マッチ全体が与えられています。

gsub関数は、文字列中のあるパターンの出現箇所をすべて別のものに置き換えるために使うことができます。最初の2つの引数は文字列とパターン、3番目の引数は置換する文字列、4番目の引数は置換する最大出現回数です。第3の引数は、文字列のほかに、表や関数でもよい。

第3引数が文字列の場合、それは置換文字列と呼ばれ、文字列内のパターンの出現を置き換えます。パターンによって格納されたキャプチャは,置換文字列の中に埋め込むことができ, パーセント記号の後にキャプチャの番号を表す数字が続きます。マッチ自体は、%0で表すことができます。置換文字列中のパーセント記号は,%%としてエスケープされなければなりません。

第3引数がテーブルの場合、最初のキャプチャはそのテーブルをインデックスするキーとして使われ、置換文字列はテーブルのそのキーに対応する値になります。関数の場合は、すべてのキャプチャが引数として渡され、マッチするごとにその関数が呼び出されます。どちらの場合も、キャプチャがない場合は、代わりにマッチ全体が使われます。関数やテーブルがfalseやnilを与えた場合、置換は行われません。

以下は、Lua 5.2 Reference Manualから直接引用した例です。

x = string.gsub("hello world", "(%w+)", "%1 %1")
--> x="hello hello world world"

x = string.gsub("hello world", "%w+", "%0 %0", 1)
--> x="hello hello world"

x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
--> x="world hello Lua from"

x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
--> x="home = /home/roberto, user = roberto"

x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
      return load(s)()
    end)
--> x="4+5 = 9"

local t = {name="lua", version="5.2"}
x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
--> x="lua-5.2.tar.gz"
—Lua authors, Lua 5.2 Reference Manual

Luaには、パターンマッチングの関数以外にも、文字列を操作するための関数が用意されています。文字の順番を逆にした文字列を返すreverse関数、文字列の小文字を返すlower関数、文字列の大文字を返すupper関数、文字列の長さを返すlen関数、引数で与えられた2文字の位置で始まり、その位置で終わる文字列の部分列を返すsub関数などです。このほかにもさまざまな関数があり、その説明はリファレンスマニュアルに記載されています。


脚註[編集]

  1. ^ Ierusalimschy, Roberto; Celes, Waldemar; Henrique de Figueiredo, Luiz.『Lua 5.4 Reference Manual』.