ここではLuaにおける条件分岐(if文,関係演算子,論理演算子)とコマンド行引数についてを扱う。
プログラムを書く際には変数の値などの特定の条件によって分岐を行って別々の処理を行いたい場面があるが、そこで用いるのがif
文となる。
以下、分岐の幾つかのパターンを扱うが、条件部分の記述については後述する。また、条件を満たした場合・満たさない場合の処理の記述は見やすさのために改行や字下げをして記述しているが、これは文法上は必須ではなく、1行におさめる書き方もできる。
(1行で記述する例)
$ lua -e "if true then print('true') end"
true
if
文の最も単純な形は下のようになり、条件を満たした場合にのみ特定の(then
とend
の間に記述した)処理を行うようにできる。
if [条件] then
[条件を満たした場合の処理]
end
条件を満たさなかった場合はthen
とend
の間の処理を飛ばしてend
の後ろの処理に進む。
条件を満たした場合と満たさなかった場合とで別々の処理を行いたい場合は下のように記述する。
if [条件] then
[条件を満たす場合の処理]
else
[条件を満たさない場合の処理]
end
上の書き方では1つの条件評価についてしか分岐できないが、最初のif
で条件を満たさなかった場合に続けて(最初のif
の分岐に対応する別の)条件の評価を行っていくことができる。
if [条件1] then
[条件1を満たした場合の処理]
elseif [条件2] then
[(条件1を満たさず)条件2を満たした場合の処理]
elseif [条件3] then
[(条件2までを満たさず)条件3を満たした場合の処理]
elseif [条件4] then
[(条件3までを満たさず)条件4を満たした場合の処理]
...
else
[いずれの条件も満たさない場合の処理]
end
この形の分岐で使われるelseif
の部分は言語によって記述が異なり、Luaの場合はelse if
でもelif
でもなくelseif
となる。else if
とスペースを入れる形になるのはelse
の中の最初にif文が入る形のみで、上の分岐の構造とは異なる。
$ lua -e "if false then print('true') else if true then print('false, true') else print('false, false') end end"
false, true
上で “[条件]” として記述してきた条件部分は、何かの条件を “満たす” か “満たさないか” の2通りの状態のいずれかを入れて評価する。
Luaのデータ型には真偽値型という “2通りの状態のいずれかを示す” ための型があり、true
(真)とfalse
(偽)のいずれかをとる。このいずれかを値として新しい変数に入れると、その変数は真偽値型になる。
既に本記事内のコマンド実行での例で記述しているように、この値それ自体をif
文の条件部分に入れて評価することができる(true
のときに条件を満たし、false
のときには満たさない)。
$ lua -e "e = true; if e then print('true') else print('false') end"
true
$ lua -e "e = false; if e then print('true') else print('false') end"
false
真偽値型ではない型は、その内容に関わらず “条件を満たす” と扱われる。
(空文字列の場合)
$ lua -e "e = ''; if e then print('true') else print('false') end"
true
(0の場合)
$ lua -e "e = 0; if e then print('true') else print('false') end"
true
特殊な値nil
や未使用の変数名をif
文の条件部分に入れると、 “条件を満たさない” と扱われる。
(nilの場合)
$ lua -e "e = nil; if e then print('true') else print('false') end"
false
(未使用の変数名の場合)
$ lua -e "e = 1; if f then print('true') else print('false') end"
false
2つの値(変数や定数)どうしを “関係演算子” という種類の演算子のいずれかで比較することでその比較結果を真偽値として得ることができる。
(1と1の比較)
$ lua -e "print(1 == 1)"
true
(1と2の比較)
$ lua -e "print(1 == 2)"
false
以下はLuaにおける関係演算子の一覧となるが、左右が異なる場合にtrue
となる~=
演算子は、他の言語では!=
となっていることが多く、注意が必要。
演算子 | 比較結果が真(true)になる条件 |
---|---|
== | 左右の型と値が等しい |
~= | 左右の型もしくは値が異なる |
< | 左の値が右の値より小さい |
> | 左の値が右の値より大きい |
<= | 左の値が右の値より小さいか同じ |
>= | 左の値が右の値より大きいか同じ |
if
文を実際に用いる際にはこの関係演算子による比較が行われることが多い。下の例は動作確認用で実用的ではなく、実際には比較する片方もしくは両方に変数が用いられる。
$ lua -e "if 4 > 1 then print('4は1より大きい') else print('4は1より小さい') end"
4は1より大きい
$ lua -e "if 7 % 2 == 0 then print('7は2で割り切れる') else print('7は2で割り切れない') end"
7は2で割り切れない
複数の条件を同時に満たすかどうかで分岐したい場合は
if [条件1] and [条件2] then
[条件を同時に満たした場合の処理]
end
として複数の条件を論理演算子のand
で結ぶ。上の “[条件2]” の後ろにand
を付けて3つ以上の条件を同時に満たすかどうかで分岐するようにもできる。他の言語ではand
は&&
と書くこともあるが、Luaではand
としてしか書けない。
複数の条件の内、いずれか1つの条件を満たすかどうかで分岐したい場合は
if [条件1] or [条件2] then
[条件のいずれかを満たした場合の処理]
end
として複数の条件を論理演算子のor
で結ぶ。これも “[条件2]” の後ろにor
を付けて条件を3つ以上(の中のいずれか)にすることもできる。他の言語ではor
は||
と書くこともあるが、Luaではor
としてしか書けない。
条件は計算と同様に丸括弧でまとめることができ、人間にとって読みやすいものにもなる。論理演算子には優先順位(or
よりand
が高く、後述のnot
は更に高い)があるが、意図した条件が分かりやすいように記述するために括弧は積極的に使ったほうがよい。
and
やor
による複数の条件の評価1では、全ての条件が必ずしも評価されるわけではなく、結果が確定した段階で評価を終える2。
[条件1] and [条件2]
で条件1がfalse
やnil
になった場合は(この段階で両方を満たすことはありえなくなって)条件2を評価しなくても結果が条件1の内容(false
やnil
)であることが確実になるため、条件2の評価は行わない[条件1] or [条件2]
で条件1がtrue
になった場合は(この段階で片方が満たされたために)条件2を評価しなくても結果がtrue
であることが確実になるため、条件2の評価は行わない(1つ目の条件で結果が決まらない場合/2つ目の条件が評価されprint()が呼ばれる)
$ lua -e "print(true and print('abc'))"
abc
nil
$ lua -e "print(false or print('abc'))"
abc
nil
(1つ目の条件で結果が決まる場合/2つ目の条件が評価されずprint()が呼ばれない)
$ lua -e "print(false and print('abc'))"
false
$ lua -e "print(nil and print('abc'))"
nil
$ lua -e "print(true or print('abc'))"
true
論理演算子のnot
を用いると条件を逆にすることができる。
(trueの逆を評価する)
$ lua -e "if not true then print('true') else print('false') end"
false
(falseの逆を評価する)
$ lua -e "if not false then print('true') else print('false') end"
true
Luaスクリプトへのコマンド行引数は “おみくじ/複数の文字列の内の1つを無作為に選択する” で扱ったテーブル型のarg
という名前の変数で得られ
#arg
arg[0]
arg[1]
,arg[2]
,…として取り出せる。例えば3つの引数を付けて
$ lua [スクリプトの場所] aaa bbb ccc
として実行した場合は
#arg
は3
arg[1]
はaaa
arg[2]
はbbb
arg[3]
はccc
となる。
スクリプトファイルを用いずにシェルを用いて動作を確認すると
$ echo "print(('#arg:%d arg[0]:%s arg[1]:%s arg[2]:%s arg[3]:%s'):format(#arg, arg[0], arg[1], arg[2], arg[3]))" | lua - aaa bbb ccc
#arg:3 arg[0]:- arg[1]:aaa arg[2]:bbb arg[3]:ccc
のようになる3。
引数に数値を指定するプログラムでは、arg[1]
などのように取り出した個別の引数は文字列型となっているため、これを数値として扱いたい場合はtostring()
関数にその文字列を入れて数値としての値を戻り値として取り出して用いる。
ただし数値として有効でない文字列(例:abc
)を入れた場合にはこの関数は特殊な値nil
を返すので、コマンド行引数のように実行時に自由に内容が決められるものは数値にする際にnil
かどうかのチェックをするのがよい。
n = tonumber(arg[1])
if not n then
io.stderr:write('ERROR MESSAGE\n')
os.exit(false) -- 失敗の終了ステータスで終了
end
上の例では数値でない場合にos.exit()
関数を用いてプログラムを異常終了(処理に失敗して終了)したものとして(OSへ伝えて)終了している。
下のコードはif
文とコマンド行引数の処理の例となる。
006_if_and_args.lua
エンコーディング:UTF-8
#! /usr/bin/lua
-- -*- coding: utf-8 -*-
--[[-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
動作確認バージョン: 5.3, JIT2.1
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --]]
-----
----- 条件分岐(if文,関係演算子,論理演算子)とコマンド行引数
-----
do
-- 引数はテーブルargから取り出せる
-- 追加の引数があるかどうかは#argの値で確認可
-- arg[0]でスクリプトのパス名が文字列として取り出せる
-- 「if [条件] then [条件を満たした場合の処理...] end」形式で
-- 特定の条件を満たした場合のみ処理を行うことができる
-- このプログラムは2つのコマンド行引数を必須とする
-- 「[値1] ~= [値2]」は2つの値もしくは型が異なる場合に真となる
if #arg ~= 2 then
print(('使い方: %s [数字1] [数字2]'):format(arg[0]))
-- 引数に指定した値(真偽値可・引数省略時true)を戻り値として実行を終了
os.exit(false) -- 失敗の終了ステータスで終了
end
-- それぞれの引数は文字列型になっているので
-- 数値として扱うためにtonumber()関数に入れる
local a = tonumber(arg[1])
local b = tonumber(arg[2])
-- tonumber()に数値文字列以外を入れると値「nil」が入るので
-- 数値かどうかのチェックができる
-- 値が「nil」である条件は「not [変数]」となる
-- ここではaかbのいずれかがnilのときに条件が真となるように
-- 「not a or not b」とした
if not a or not b then
io.stderr:write('数値ではない入力があります\n')
os.exit(false)
end
-- 1つ目の値が整数かどうかの確認
-- math.floor()は引数の値の小数点以下を切り捨てた値を得る
if math.floor(a) ~= a then
print(('1つ目の値 (%s) は整数ではありません'):format(a))
-- 「else」の後ろ(「end」の前まで)は
-- 上のifの条件に当てはまらなかった場合に実行される
-- この場合はaが整数の場合
else
-- 偶数か奇数かのチェック
-- 2で割って余りが0なら偶数
if a % 2 == 0 then
print(('1つ目の値 (%s) は偶数です'):format(a))
else
print(('1つ目の値 (%s) は奇数です'):format(a))
end
end
local c = a + b
-- 2つの値の合計の値の範囲による分岐を行う
if c > 100 then
print(('2つの値の合計 (%s) は100より大きいです'):format(c))
-- 「elseif」は対応する最初のifの条件に該当しなかった場合に条件が評価され
-- 条件を満たせば下の処理が実行される
elseif c > 0 then
print(('2つの値の合計 (%s) は0より大きく100以下です'):format(c))
elseif c > -100 then
print(('2つの値の合計 (%s) は-100より大きく0以下です'):format(c))
-- 最初の「if」とこれに対応する後ろの全ての「elseif」で条件を満たさない場合に
-- 「else」と「end」の間の処理が実行される
else
print(('2つの値の合計 (%s) は-100以下です'):format(c))
end
-- 複数の条件が全て真のときにのみ条件を満たすようにするには
-- 複数の条件を「and」でつなげる
if a > 100 and b > 100 and c > 1000 then
print(('1つ目の値 (%s) と2つ目の値 (%s) はともに100より大きく、かつ合計 (%s) は1,000より大きいです'):format(a, b, c))
end
-- 条件は丸括弧でまとめて組み合わせることもできる
if (a == 100 or b == 100) and c == 150 then
print(('1つ目の値 (%s) と2つ目の値 (%s) のいずれかが100で、かつ合計 (%s) が150です'):format(a, b, c))
end
-- 関係演算子「==」による比較では両方が同じ型でないと偽となる
-- aはtonumber()で数値型にしているので
-- ここで文字列「100」と一致することはない
if a == '100' then
print(('1つ目の値 (%s) は"100"です'):format(a))
else
print(('1つ目の値 (%s) は"100"ではありません'):format(a))
end
end
-- 終了