Lua/文
文(Statements)は、実行可能なコードの一部であり、使用する命令(instruction)と式(expressions)が含まれています。一部の文には、たとえば特定の条件下で実行される可能性のあるコードも含まれます。式とは異なり、コードに直接入れることができ、実行されます。Luaにはいくつかの命令がありますが、これらの命令は、他の命令や複雑な式と組み合わせることで、ユーザーに十分な制御と柔軟性を提供します。
代入
[編集]プログラマーは、後で使用できるように、値をメモリーに格納できる必要があることがよくあります。 これは変数を使用して行われます。変数は、コンピューターのメモリに格納されている値への参照です。 後でメモリーに保存した後、数値にアクセスするために使用できます。 代入(Assignment)</ dfn>は、変数に値を割当てるために使用される命令です。 これは、値が格納される変数の名前、等号、および変数に格納される値で構成されます。
variable = 43 print(variable) --> 43
上のコードで示したように、変数の値をアクセスしたい場所に変数名を入れることで、その値にアクセスすることができる。
代入演算子
[編集]Luaでは、他の多くのプログラミング言語と同様に、等号(=
)は二項の代入演算子(assignment operator)として機能し、右側のオペランドの式の値を左側のオペランドで指定した変数に代入します。
変数への代入
[編集]次の例は、変数の代入に等号を使用するものです。
fruit = "apple" -- 文字列を変数に代入 count = 5 -- 数値を変数に代入
文字列と数値
[編集]なお、リテラル文字列は変数名と区別するために、引用符で囲む必要があります。
apples = 5 favourite = "apples" -- 引用符で囲まない場合、apples は変数名として解釈されます。
なお、変数名は数字で始めることができないため、数値は引用符で囲む必要がなく、変数名と誤認されることはありません。
apples = 6 -- 数値パラメータを囲む引用符は必要ありません。 pears = "5" -- 引用符で囲むと、その値は文字列とみなされます。
複合代入
[編集]Luaは、複合代入(multiple assignments)をサポートしています。
apples, favorite = 5, "apples" -- apples に 5 を、favorite に "apples" を代入
識別子
[編集]Luaでは、識別子(Identifiers)のことを "名前"とも呼びます。文字、数字、アンダースコアで構成され、数字で始まらない任意のテキストを使用することができます。変数やテーブルのフィールドの名前に使われます。テーブルの章で説明します。
以下に有効な名称を示します。
name
hello
_
_tomatoes
me41
__
_thisIs_StillaValid23name
以下は、無効な名前です。
2hello
: starts with a digitth$i
: contains a character that isn't a letter, a digit or an underscorehel!o
: contains a character that isn't a letter, a digit or an underscore563text
: starts with a digit82_something
: starts with a digit
また、以下のキーワードはLuaで予約されているため、名前として使用することはできません。and
, break
, do
, else
, elseif
, end
, false
, for
, function
, if
, in
, local
, nil
, not
, or
, repeat
, return
, then
, true
, until
, while
.
変数またはテーブルフィールドに名前を付けるときは、その有効な名前を選択する必要があります。したがって、文字またはアンダースコアで始まり、文字、アンダースコア、および数字のみを含める必要があります。Luaでは大文字と小文字が区別されることに注意してください。これは、 Hello
とhello
が2つの異なる名前であることを意味します。
スコープ
[編集]変数のスコープ(Scope)は、その変数が意味を持つプログラムのコードの領域です。これまでに見た変数の例はすべてグローバル変数の例であり、プログラムのどこからでもアクセスできる変数です。一方、ローカル変数は、それらが定義されたプログラムの領域から、およびプログラムのその領域内にあるプログラムの領域でのみ使用できます。これらはグローバル変数とまったく同じ方法で作成されますが、接頭辞としてlocal
キーワードを付ける必要があります。
do
文は、それらを説明するために使用されます。do
文は、新しいコードブロック、つまり新しいスコープを作成する以外の目的がない文です。 end
キーワードで終わります。
local variable = 13 -- これは、メイン領域で定義されたので、スクリプトのどこからでもアクセスできるローカル変数を定義しています。 do -- この文は、新しいブロックと、新しいスコープを作成します。 variable = variable + 5 -- これにより、変数variableに5が追加され、18となります。 local variable = 17 -- これは前の変数variableと同じ名前の変数variableを作成しますが、この変数variableはdo文が作成したスコープにローカルに存在するものです。 variable = variable - 1 -- これにより、ローカル変数variableから1が引かれ、16となる。 print(variable) --> 16 end print(variable) --> 18
スコープが終了すると、スコープ内のすべての変数が削除されます。コードの領域は、含まれているコードの領域で定義された変数を使用できますが、同じ名前のローカル変数を定義して変数を「上書き」すると、コードの他の領域で定義された変数の代わりにそのローカル変数が使用されます。これが、print関数の最初の呼び出しが16を出力し、do
文によって作成されたスコープ外の2番目の呼び出しが18を出力する理由です。。
なぜなら、ローカル変数はグローバル変数のように現在の関数の環境に格納されるのではなく、レジスターに格納されるため、グローバル変数よりも高速に定義、アクセスすることができるからです。レジスターとは、Luaがローカル変数に素早くアクセスするために保存するための領域で、通常200個までのローカル変数しか格納することができません。すべてのコンピュータの重要な構成要素であるプロセッサーにもレジスターがありますが、これはLuaのレジスターとは関係ありません。また、各関数(プログラムの核となるメインスレッドを含む、これも関数)は独自の環境を持っており、これは変数名にインデックスを使用し、そのインデックスに対応する値にこれらの変数の値を格納するテーブルである。
代入の形式
[編集]補強代入
[編集]補強代入(Augmented assignment)</ dfn>は、複合代入(compound assignment)</ dfn>とも呼ばれ、変数に以前の値に相対的な値を与える代入の一種です。値。たとえば、現在の値をインクリメントします。C言語に存在することがわかっているaの値を8ずつインクリメントするコードa+ = 8
に相当します。JavaScript、Ruby、Pythonなどには存在しますが、はLuaに存在しないため、必要です。 a = a + 8
と記述します。
連鎖代入
[編集]連鎖代入(Chained assignment)とは、多くの変数に1つの値を与える代入の一種です。例えば、a = b = c = d = 0
というコードは、CやPythonではa, b, c と dの値を0に設定することになります。Luaでは、このコードはエラーになるので、先の例をこのように書く必要があります。
d = 0 c = d -- あるいは c = 0 b = c -- あるいは b = 0 a = b -- あるいは a = 0
並列代入
[編集]並列代入(Parallel assignment)は、同時代入(simultaneous assignment)や多重代入(multiple assignment)とも呼ばれ、異なる変数に異なる値(同じ値でも可)を同時に代入する代入の一種です。連鎖式代入(chained assignmen)や補強代入(augmented assignment)と異なり、Luaでは並列代入が利用できます。
前節の例は、並列代入を使うように書き換えることができます。
a, b, c, d = 0, 0, 0, 0
値よりも多くの変数を指定した場合、一部の変数には値が代入されません。もし変数よりも多くの値を指定した場合、余分な値は無視されます。つまり、余分な値は削除され、変数のリストと同じ長さになるように末尾に余分な nil 値が追加されます。関数呼び出しが値リストの最後に存在する場合、関数呼び出しが括弧でくくられない限り、その関数呼び出しが返す値はリストの最後に追加される。
また、一般的なプログラミング言語と異なり、Luaは順列処理(permutation)によって変数の値を再代入することができます。例えば
first_variable, second_variable = 54, 87 first_variable, second_variable = second_variable, first_variable print(first_variable, second_variable) --> 87 54
これが機能するのは、代入文が何かを代入する前にすべての変数と値を評価するためです。代入は、実際に同時であるかのように実行されます。つまり、新しい値が代入られる前に、変数とその変数の値でインデックス付けされたテーブルフィールドに同時に値を代入することができます。つまり、次のコードは、dictionary[1]
ではなくdictionary[2]
を12に設定します。
dictionary = {} index = 2 index, dictionary[index] = index - 1, 12
条件文
[編集]条件文は、式が真であるかどうかをチェックし、真である場合は特定のコードを実行する命令(instructions)です。式が真でない場合は、そのコードをスキップしてプログラムを続行します。Luaでは、唯一の条件文はif
命令を使用します。falseとnilは両方とも偽と見なされ、他のすべては真と見なされます。
local number = 6 if number < 10 then print("数値 " .. number .. " は、10未満です。") end -- 他のコードがここにあっても、条件文のコードが実行されたかどうかに関係なく実行されます。
上記のコードでは、変数 numberに代入文で数値6が割り当てられています。次に、条件文は、変数 number に格納されている値が10より小さいかどうかをチェックします。これは、そのケースです。この場合は、"数値 6 は、10未満です。"を出力します。
また、キーワードelse
を使うことで、式が真でなかった場合のみ、あるコードを実行することができ、キーワードelseif
で条件文を連鎖させることも可能です。
local number = 15 if number < 10 then print("number は、10より小さい。") elseif number < 100 then print("number は、10以上、100未満。") elseif number ~= 1000 and number < 3000 then print("number は、100以上、3000未満、1000以外。") else print("number は、1000または3000以上。") end
- 注意
else
ブロックは常に最後のブロックである必要があります。else
ブロックの後にelseif
ブロックを含めることはできません。elseif
ブロックは、その前にあるブロックが実行されなかった場合にのみ意味があります。
2つの値を比較するために使用される演算子は、上記のコードで使用されているものもあり、関係演算子と呼ばれます。関係が真の場合、論理値true
を返します。それ以外の場合は、論理値false
を返します。
比較演算子 等しい 等しくない 大きい 小さい 以上 以下 数学的表記法 = ≠ > < ≥ ≤ Luaの演算子 == ~= > < >= <=
上記のコードは、and
キーワードを使用して、条件式で多くのブール式を組み合わせる方法も示しています。
ループ
[編集]プログラマーは頻繁に、あるコードや類似のコードを何度も実行したり、あるコードをユーザーの入力に依存して何度も実行したりする必要があります。ループ(Loops)とは、一度だけ指定され、連続して何度も実行される可能性のある一連の記述のことです。
条件制御付きループ
[編集]条件制御ループ(Condition-controlled loops)は、条件によって制御されるループです。これらは条件付き文に非常に似ていますが、条件がtrueの場合にコードを実行し、それ以外の場合はスキップする代わりに、条件がtrueの間、または条件がfalseになるまでコードを実行し続けます。Luaには、条件制御ループ用の2つの文があります。while
ループとrepeat
ループです。このようなループはコードを実行し、条件が真であるかどうかを確認します。真の場合、コードを再度実行し、条件が偽になるまで繰り返します。条件が偽の場合、コードの繰返しを停止し、プログラムフローが続行されます。コードの各実行は、反復と呼ばれます。while
ループとrepeat
ループの違いは、repeat
ループは、ループの最後で状態をチェックすることです。while
ループは、ループの開始時にそれをチェックします。これは最初の反復でのみ違いがあります。repeat
ループは、コードが最初に実行されたときに条件が偽であっても、常に少なくとも1回はコードを実行します。これは、while
ループには当てはまりません。このループは、条件が実際に真である場合にのみ、コードを最初に実行します。
local number = 0 while number < 10 do print(number) number = number + 1 -- number を1つ増やす。 end
上記のコードは、0、1、2、3のように、9。10回目の反復後、numberは10以上になるため、ループの実行は停止します。ループは永久に実行されることを意味する場合があります。その場合、ループは無限ループと呼ばれます。たとえば、レンダラー、つまり画面上に物を描くソフトウェアプロセスは、ユーザーに表示される画像を更新するために画面を再描画するために絶えずループすることがよくあります。これはビデオゲームでよくあることです。ビデオゲームでは、ユーザーに表示される内容を常に最新の状態に保つために、ゲームビューを常に更新する必要があります。ただし、ループを永久に実行する必要がある場合はまれであり、そのようなループはエラーの結果であることがよくあります。無限ループは多くのコンピューターリソースを消費する可能性があるため、ユーザーから予期しない入力を受け取った場合でも、ループが常に終了するようにすることが重要です。
local number = 0
repeat
print(number)
number = number + 1
until number >= 10
上記のコードは、上記のwhile
ループを使用したコードとまったく同じことを行います。主な違いは、条件がwhile
キーワードとdo
キーワードの間に置かれるwhile
ループとは異なり、条件は次のように置かれることです。until
キーワードの後のループの終わり。repeat
ループは、ブロックを作成し、end
キーワードで閉じられないLuaの唯一の文です。
カウント制御ループ
[編集]変数をインクリメントすると、その値が段階的に、特に1段階ずつ増加します。前のセクションの2つのループは、変数numberをインクリメントし、それを使用してコードを特定の回数実行しました。この種のループは非常に一般的であるため、Luaを含むほとんどの言語には組み込みの制御構造があります。この制御構造はカウント制御ループ(Count-controlled loops)と呼ばれ、Luaおよびほとんどの言語では、for
文によって定義されます。このようなループで使用される変数は、ループカウンター(loop counter)と呼ばれます。
for number = 0, 9, 1 do print(number) end
上記のコードは、前のセクションで示した2つのループとまったく同じことを行いますが、number変数はループのローカルであるため、ループ内からのみアクセスできます。変数名と等号記号に続く最初の数字は初期化です。これは、ループカウンターが初期化される値です。2番目の番号は、ループが停止する番号です。変数をインクリメントし、変数がこの数に達するまでコードを繰り返します。最後に、3番目の数値は増分です。これは、各反復でループカウンターが増加する値です。増分が指定されていない場合、Luaによって1と見なされます。したがって、以下のコードは1, 1.1, 1.2, 1.3, 1.4および 1.5。
for n = 1, 2, 0.1 do print(n) if n >= 1.5 then break -- ループを即座に終了させ、繰返さない。 end end
上のコードが2まで上がらず、1.5までしか上がらないのは、ループを即座に終了させるbreak
文があるからです。この文は、while
ループやrepeat
など、どんなループでも使うことができる。なお、ここでは>=
演算子が使われていますが、理論的には==
演算子でも同じことができます。これは、小数点の精度に誤差があるためです。Luaは倍精度浮動小数点方式で数値を表現しており、実際の値の近似値としてメモリに格納されます。近似値は数値と完全に一致する場合もありますが、近似値でしかない場合もあります。通常、これらの近似値は十分に数値に近いので違いはありませんが、このシステムでは等号演算子を使用する際にエラーが発生する可能性があります。このため、一般に10進数を扱う場合は、等号演算子を使わない方が安全です。この具体的なケースでは、==
演算子を使うとコードが動かなくなりますが[1](1.9まで続いてしまう)、>=
演算子を使うと動きます。
ブロック
[編集]ブロック(Blocks)は、順次実行される文のリストです。これらの文には、何も命令を含まない空の文を含めることができます。空の文は、セミコロンでブロックを開始したり、2つのセミコロンを順番に記述するために使用することができます。
関数呼び出しと代入は括弧で始まることがありますが、これは曖昧さを生じさせます。この断片はその一例です。
a = b + c
(print or io.write)('done')
このコードには2つの解釈があります。
a = b + c(print or io.write)('done')
a = b + c; (print or io.write)('done')
現在のパーサーは、このような構文を常に最初の方法で見て、開始括弧を呼び出しの引数の開始として解釈しています。この曖昧さを避けるために、常に括弧で始まる文の前にセミコロンを置くのが良い方法です。
;(print or io.write)('done')
チャンク
[編集]Luaのコンパイル単位をチャンク(Chunks)と呼びます。チャンクはファイルやホストプログラム内の文字列に格納することができます。チャンクを実行するには、まずチャンクを仮想マシン用の命令にプリコンパイルし、次に仮想マシン用のインタプリタを使ってコンパイルされたコードを実行します。チャンクは、Luaに付属するコンパイルプログラムluac
や、与えられた関数のバイナリ表現を含む文字列を返すstring.dump
関数を使って、バイナリ形式(バイトコード)にプリコンパイルすることも可能です。
load関数はチャンクをロードするために使用されます。load
関数の最初のパラメータが文字列の場合、チャンクはその文字列となります。この場合、文字列は Lua コードまたは Lua バイトコードのいずれかになります。最初のパラメーターが関数の場合、load
はその関数を繰返し呼び出してチャンクの断片を取得します。各断片は前の文字列と連結される文字列です。そして、何もないか空文字列が返されると、そのチャンクは完了したとみなされます。
load
関数は、構文エラーがない場合、コンパイルされたチャンクを関数として返します。それ以外の場合は、nilとエラーメッセージが返されます。
load
関数の 2 番目のパラメータはチャンクのソースを設定するために使用されます。すべてのチャンクは、適切なエラーメッセージやデバッグ情報を提供するために、ソースのコピーを保持しています。デフォルトでは、ソースのコピーは load
に与えられたコードになります(コードが与えられた場合、代わりに関数が与えられた場合、それは "=(load)" になります)。このパラメータは、それを変更するために使用することができます。これは、コードをコンパイルするときに、元のソースを取返されないようにするために使うのがほとんどです。その場合、バイナリ表現に含まれるソースを削除する必要があります。そうしないと、元のコードをそこで入手することができるからです。
load関数の第3パラメータは生成される関数の環境を設定するために使用され、第4パラメータはチャンクがテキストであるかバイナリであるかを制御します。文字列 "b" (バイナリチャンクのみ)、 "t" (テキストチャンクのみ)、または "bt" (バイナリおよびテキスト両方) が指定できます。デフォルトは "bt"です。
loadfile
関数もあります。これはload
と全く同じように動作しますが、代わりにファイルからコードを取得します。最初のパラメーターは、コードを取得するファイルの名前です。バイナリ表現に格納されたソースを変更するパラメータはなく、load
関数の第3、第4パラメータがこの関数の第2、第3パラメータに相当します。loadfile
関数は、標準入力からコードを読み込むためにも使用でき、ファイル名が与えられていない場合は、これが実行されます。
dofile
関数はloadfile
関数と似ていますが、ファイル内のコードを関数としてロードするのではなく、ソースコード・ファイルに含まれるコードをLuaチャンクとして即座に実行します。唯一のパラメータは、実行するファイルの名前を指定するために使用されます。引数が与えられない場合、標準入力の内容が実行されます。チャンクが値を返す場合、その値は dofile 関数の呼び出しによって提供されます。dofile
は protected モードで実行されないので、それを通して実行されたチャンクのすべてのエラーは伝搬します。