ってことで第4回
さー今回もめんどいぞー(主に書いてる俺が(死


式について

こー・・・れ代入を説明するより前にやるべきだったかも;
えっと、まぁでも、結局数学のと変わりないんだけどね〜w
前回の代入の続きってことでちょろっと補足しよっかな
a,b,c = f(),x
はい、まぁa、b、cの3つの変数に対する多重代入だね
ただ、代入されるものがf()・・・つまり、関数が呼び出されてるわけだ
どうなるかと言うと、テーブルの初期化のときと一緒です
関数f()の最初の戻り値だけがaに入って、xがbに入る、そんだけ
多重代入のルールどおりcはnilのまま
逆に、
a,b,c = x,f()
ってすれば、これもテーブルの初期化と一緒でaにxが入って、f()の戻り値が順にb,cに入る
f()に3つ以上戻り値があればこれも多重代入のルールどおり、あふれた分はガン無視っと
前回までを理解できてれば簡単簡単w
ではここで問題
g(f(),x)
g(x,f())
この2つの関数呼び出しは一体どうなるでしょう?
・・・まぁ、ルールは一緒だけどNEw
前者はf()の最初の戻り値1個だけとxの2つがg()の引数として渡される
後者はxとf()の戻り値全部がg()の引数として渡される
無論、あふれればガン無視w

演算子について

演算子ってのはー・・・
式を区切る記号・・・ん〜・・・ま要は数学での+−×÷とかだ(死
難しく考えることもない(ぇー
・・・んだが、プログラミングにおいてはやっぱ普通数学じゃ使わないようなものも一部あるわけで
ちなみに演算の対象にされる値のことをオペランドと呼ぶ
例を挙げると、
i + 1
って式があったら+が演算子で、iと1がそれぞれこの+に対するオペランドってことになる
ならもし
i + j + k + l
って式があったらまず何処から計算される?
まぁ同じ+演算子を3つ使ってるだけだから、数学と同じで単純に左端、つまりi+jの部分から順に計算されていくことになる
この、「同じ演算子が並んだ時、どちら側から先に計算されるのか」の法則を、左側から順番に計算されることを左結合、右側から順に計算されることを右結合と呼ぶ
Luaの場合、基本的にはほとんどの演算子が左結合
右結合は2種類しかない
どれが右結合かはまぁそいつが出てきた時にw
演算子ごとにいくつか分類もあるので1個ずつ見ていこう

算術演算子

なんか難しい書き方したけどふつーの数学の四則演算ですw
+(加算)、-(減算)、*(乗算)、/(除算)問題なく全部できますよ〜
-1とか-2とかマイナスの値も普通に使える
ただし式の先頭以外に書く時は(-1)っと囲ってやらないとダメかな
減算記号としての-と区別がつけられないからね〜
それと、
a % b
と書くことでa/bの余りを求めることができる
a ^ b
と書けばaのb乗の計算ができる
この累乗演算子が右結合2種類の1つ目
a ^ b ^ c
とあったらまずbのc乗を計算してから、その答えでaを累乗するって流れになるね

関係演算子

これはその名のとおり、2つの式の関係を比較する演算子
式の結果は必ずtrueかfalseかで返ってくる
まずはこれ
a == b
これは2つが同じものかどうかを比較する
aの中身とbの中身が同じだったらtrue、別物ならfalse
こいつが呼ばれると、まずオペランドの型を比較する
ここでもし
a = 1
b = "1"
だったらこの時点でfalseなわけだ
同じ1でもaは数値、bのほうはダブルクォートで囲んでるから文字列扱いだもんね
めでたく型が両方同じだったらここで初めて中身の値が比較されることになるわけだが
ここで1つ注意点
例えば、
t = {1, 2, 3}
a = t
b = {1, 2, 3}
としよう
さてこのt、a、bの3つのテーブル
3つとも一見すれば要素それぞれの中には全部同じ値が入った全く同じテーブルだ
けど、これを等価比較してみると
x = t == a
y = t == b
z = a == b
この3つのうちtrueで返ってくるのはxだけになる
この違いは何かと言うと、テーブルの説明の時に書いた「コピペ」か「ショートカットの作成」かの違いだったりする
まぁつまりこの==って演算子は要素各々の中身の値じゃなく「ショートカットのリンク先が何処なのか」を比べてるわけですな
だからtとaは同じテーブルに対するショートカットリンクだからtrue
bは、含まれる要素自体はtとaと全く同じものだけど、新しく別のテーブルを用意してそこに同じ値を突っ込んだだけの別物って扱いだからfalseなわけ
ちなみに言い忘れてたけど、関数を代入するのも「ショートカットの作成」になるからこれと同じ法則が当てはまるよん
ややこしいねぇ・・・w
ま、そろそろ次いこうか
a ~= b
こいつは==の反対を意味する
つまりここまでの説明を全部真逆にしたのがこいつ
別物ならtrue、同じならfalseが返ってくる
残るは
a > b
aのほうが大きければtrue、そうでないならfalse
a < b
aのほうが小さければtrue、そうでないならfalse
a >= b
aとbが同じかaのほうが大きければtrue、(ry
a <= b
aとbが同じかaのほうが小さけれb(ry
これは単純に数学と一緒〜
>=と<=は≧と≦のことねw
さー次次〜w

論理演算子

さて、こいつを本気で解説しようとしたら論理演算とか真理値とかから説明せにゃならんわけだが
めんどいから詳しくはググれ(ぁ
つぅかこれを真面目に解説しだしたらページ1分割使うわwwwwwwwww
こまけぇこたぁいいんだよ!(AAry
論理演算子は
and or not
の3つ
型の話の時に書いた「nilとfalseのみがNo」の法則を当てはめて考えることになる
実はこのYesを真(しん)、Noを偽(ぎ)って呼ぶんだけど・・・こまk(ry
まぁ以降、ここでも真か偽かって書くことにするw
とりあえず話を戻して・・・まずは論理積演算子andから
andは、
x and y
の時、xがfalseかnilならxを返す
そうでなければyを返す
具体例をいくつか
a = nil and false --偽 and 偽
b = false and 10  --偽 and 真
c = 10 and nil    --真 and 偽
d = 10 and 20     --真 and 真
aは左オペランドがnilなのでnil(偽)
bは左オペランドがfalseなのでfalse(偽)
cは左オペランドが10なのでnil(偽)
dは左オペランドが10なので20(真)
ここで例に付け足しておいたコメントと答えを見比べればわかりやすいと思うが、and演算というのはオペランドが両方真の時のみ真が返ってくるようになっている
つまり、左のオペランドが偽だった場合右オペランドは真だろうが偽だろうが偽であることが確定してしまうわけだ
あえて答えの文の書き出しを全部「左オペランドが〜」で始めて書いたのはそういうことで、左オペランドが決まればほぼ答えは決まったようなものだから
Luaの場合、左オペランドが偽だった時は実際にも右オペランドのチェックはすっ飛ばされるw
次に論理和演算子or
x or y
の時、xがfalseかnil以外ならその値を返す
そうでなければyを返す
これも具体例で見ていこう
a = false or nil --偽 or 偽
b = nil or "a"   --偽 or 真
c = 10 or false  --真 or 偽
d = 10 or 20     --真 or 真
aは左オペランドがfalseなのでnil(偽)
bは左オペランドがnilなので"a"(真)
cは左オペランドが10なので10(真)
dは左オペランドが10なので10(真)
ってことで、or演算はどっちか一方でも真なら絶対に真で返ってくるようになっている
偽になるのはオペランドが両方とも偽だった時だけ
これもつまり左オペランド次第で結果はほぼ決まったようなもので、左オペランドが真だった場合、右オペランドの値はチェックされない
最後に否定演算子not
こいつは大抵
not(a or b)
って感じで否定したい部分を()で囲ってやって使うんだけど、trueとfalseを逆転させる
つまりこの例だと、a or bの結果がnilかfalseならtrue、それ以外ならfalseが返ってくるってわけだな

文字列連結演算子

さて、2つ目の右結合演算子がこれ→..
文字通り、2つの文字列をつなげる演算子
まぁぶっちゃけこれが右結合だろうが左結合だろうがあんまし関係ないよね(ぁ
str = "abc".."def"
と書いてやればstrの中身は2つくっつけた"abcdef"になる
さてここで
i = 3
str = "abc"..i
とすると?
strの中身はちゃ〜んと"abc3"になってくれます
Luaって優秀ね!w
はい次(ぁ

長さ演算子

これは#をくっつけるんだけど・・・ま実例を見たほうが早いなw
length = #"abcde"
まぁこんだけ
これでlengthには何が入ってくるかというと、文字列の長さ・・・つまるところ文字数っすね
この例だと5になる、と
んでもひとつ
t = {1, "a", 110, true, 2.3}
ってテーブルがあったとして、
length = #t
っとするとテーブルの要素数を求められる
この場合5だねぇ
ただしそれはこの例みたいにテーブルの添字[1]から順にきちんと抜け目なく値が入っていた場合
a = { [f(1)] = g; "x", "y"; x = 1, f(x), [30] = 23; 45 };
テーブルの説明の時に使った例題の配列初期化処理だね
さて、こういう途中にnilの要素が含まれるばらばらなテーブルだったらどうなってしまうか
一応わかる範囲で一番大きな添字は30だけど、
length = #a
とやっても多分30とは出てこない
なぜかってーと、この長さ演算子は配列の何処かにnilを見つけたところでそこを終端と認識してしまうから
だからこれの結果は[1]からきちんと順番に格納されている[4]までで配列の終わりとみなされて、lengthは4で返ってくることになるかもしれない(あるいはもしかすれば[30]の次のnilや、["x"]とか[f(1)]の次のnilを認識するかもしれない

演算子の優先順位

さて、これでLuaで使える一通りの演算子は説明し終わったわけだが、こいつらには実は全て優先順位がつけられている
数学で
a*b+c-d/e
って式があったらa*bとd/eの部分から先に計算してくのと理屈は一緒よ
その優先順位とやらはこんな感じ
低↑ or
 | and
 | <     >     <=    >=    ~=    ==
 | ..
 | +     -
 | *     /     %
 | not   #     - (unary)
高↓ ^
累乗が何より最優先に計算されるってことだね
次にnotと#と負数表現の-
その次に四則演算・・・これは乗算除算と剰余が加算減算に優先するのは数学と一緒だね
四則演算が終わった後に文字列が繋げられて、ここまでで左右のオペランドの計算結果が確定するってわけだ
その計算結果を元に関係演算子で比較して、そこから返されるtrueかfalseかを元にして論理演算が実行される、と
ちなみに数学と同じように()でくくればその部分を先に計算させることもできる
さぁ例題いってみようか(ぁ
(a + b) ^ 2 >= d - e / f and (g ~= h % i * j - k or l < m)
さて一体どこからどの順番で計算されるでしょうか?
まずは()でくくってあるところからだからa+b
んでもう1つの()内を優先順位どおり、h%iの結果にjをかけてからkを引く
んでg ~= (h%i*j-k)とl<mをtrueかfalseに確定させてor演算
それから(a+b)を2乗してe/f
その後にd - (e/f)を計算して
(a+b)^2 >= d-e/fをtrueかfalseか判断して
最後にその結果と、さっきのor演算の結果をand演算して終了
・・・ぶっちゃけごめんなさい(死滅
うちの説明が下手糞すぎますね_| ̄|○
( ( (a + b) ^ 2) >= (d -(e / f) ) ) and ((g ~=( ( (h % i) * j) - k) ) or (l < m) )
順序どおりの計算になるように()を付け足して色分けしてみた
・・・余計わかりにくいNE(ぁ
・・・
・・・・・・
こm;y=-( ゚д゚)・∵. ターン

制御構造

ん〜・・・まず制御構造って何よ?ってところからかしらね
例えばー・・・・・・上手い例が思いつかん(ぁ
えっと、ホムの場合で考えようw
主人とホムが十分近い距離なら、それはそれで問題ないから特に何もさせる必要ないよね?
で、主人が歩き出してホムとの距離が離れたら、ホムもそれに置いていかれないように主人を追わせなきゃいけないわけだ
そういう、一定の条件を満たした時(例の場合「主人とホムの距離が離れたら」)だけ、決まった動作(例の場合「主人を追いかける」)をさせたい!って時に、その条件と動作を決めてやるのが制御構造
Luaで使える制御構造は大きく分けて3つ
と言っても大概の他の言語での制御構造も大別すればこの3種類しかないw
条件を満たした時だけ決められた処理を実行するif文
条件を満たすまで決められた処理を繰り返すwhile、repeat文
指定した回数だけ決められた処理を繰り返すfor文
この3つ
まー1個ずつ詳しく見ていこう

if文

条件を満たした時にだけ決められた動作をするのがif文
具体的にはこうなる
if(条件式)then
  処理
end
まぁなんてことはない
()の中の条件式が真の時だけthen〜endまでの処理を行う
条件が偽ならthen〜endの間は飛ばして次に移る
ちなみに処理の部分を半角スペース2つ分下げて書いてるのは単純に読みやすくするため(このWiki、行頭の半角スペース複数を認識してくれないらしい・・・本当は全角のスペースは使っちゃダメよ?
めんどいけどこれやらんと後で混乱するのは自分だぜ?w
つかMKEditorとか秀丸辺りでも使ってりゃー普通にオートインデント機能ぐらいついてるよねぇ?
まぁいいやw
もうちょい詳しく
if(a == b)then
  x = x + 1
end

x = x + y
さてこれでどうなるかつーと
a==bが真の時だけx=x+1が実行されることになる
つまりこの場合はaとbが同じ値の時だけx=x+1が実行されるわけですね
んでそのあとx=x+yが実行される
それ以外の場合はx=x+1は無視されてx=x+yだけが実行されることになる
次にelse文
条件を満たしたらこっちの処理、でも満たさなかったら別のことをやらせたい!って時にはelseを使って
if(a == b)then
  x = x + 1
else
  x = x - 1
end
としてやれば、この例で言うならa==bが真の時にはx=x+1、偽の時にはx=x-1のほうをやってくれる
then〜endの中にさらにif文を入れ子にすることもできて、例えば
if(a == b)then
  x = x + 1
  if(x >= 5)then
    (ry)
  end
  a = a + 1
end

x = x + y
とすることもできる
こうするとまずa==bが判定されて、真ならx=x+1が実行されるところまでは一緒
で次にx>=5が判定される
この場合、xが5以上なら真だね
条件が真なら(ry)の部分が実行されてから、次のa=a+1が実行される
偽なら(ry)は飛ばしてそのままa=a+1だけを実行する
で最後にx=x+yがくるわけだ
a==bの時点で弾かれたら最後のendまですっ飛ばしてx=x+yだけ実行するのは一緒
別にどんだけ入れ子にしても特段制限もないので
if(ry)then
  if(ry)then
    if(ry)then
      if(ry)then
        if(ry
なんてことも出来ることは出来るが素人にはお勧めしないwww
話を戻して・・・
複数の条件があって、その条件ごとに別の動作をさせたい時、
if(ry)then~~
  (ry)~~
end~~
if(ry)then~~
  (ry)~~
end~~
if(ry
って書いてもまぁ別に悪いとは言わないけど、大量のifが入れ子なのか別個のものなのか後々読んだ時に判りにくいよね
そんなとき使えるのがこれ
if(条件1)then
  処理1
elseif(条件2)then
  処理2
elseif(条件3)then
  (ry
end
そのまんま、elseとifをくっつけてelseifだねw
まぁともあれこれで、まず条件1を判定、真なら処理1だけやって残りはendまで飛ばして次へ、条件1が偽なら条件2を判定して真なら処理2だけやって次へ、条件2もダメなr(ry
ってな感じに幾つでも条件を分岐させることが出来る
もちろん、どれにも当てはまらなかったら最後に普通にelseを使ってその場合の別処理をさせることもできる
例を1つ
if(a < 5)then
  x = x + 1
elseif(a > 5)then
  y = y + 1
else
  z = z + 1
まぁ見たまんま、単純明快だね
aが5より小さければxに+1 5より大きければyを+1 そのどちらでもなかった場合・・・つまり、aがちょうど5ならzに+1してるって処理だね
if文についてはこんなところかな〜

while文、repeat文

条件が満たされている間決められた動作を延々繰り返し続けるのがwhile文とrepeat文
さてその文法は
while(条件)do
  処理
end
repeat
  処理
until(条件)
どちらも、条件が真である間do〜end、repeat〜untilの間の処理を繰り返し続ける
2つの違いは見ての通り、条件を先に判定するか後で判定するか
例えば、
while(a < 5)do
  (ry)
end
と書かれた時に、最初からaが5以上の値だったら、最初のa<5で弾かれて(ry)の部分は実行されずに次に移ることになる
けど同じ条件を設定しても、
repeat
  (ry)
until(a < 5)
だと、まず無条件で1回(ry)部分は実行されてくれる
でとりあえず1回実行したあとにa<5をチェックして、そこで弾かれればループには入らずに次へと進むわけだ
whileもuntilもやっぱり好きなだけ入れ子にできるんだけど当然の如くあんまりごちゃごちゃと入れ子にしすぎるのは慣れない間はお勧めしないw

for文

さて最後、for文だ
こいつは、条件によってではなくて、指定した回数分だけ決められた動作を繰り返す
ただ、Luaのfor文には2種類あって、数値用と汎用ってのがある
まぁまずはひとまず数値用のほうから

数値用for文
for(カウンター初期化,終了条件,カウンターの増え幅)do
  処理
end
ってのが数値用for
ちょ〜いわかりにくいだろうから例を挙げて説明していこう
a = 0

for(i = 1,10,3)do
  a = a + 1
end
さて、これを実行するとどうなるかと言うと
まずはforの()の中身の説明をば
(i = 1,10,3)
まず1個目のi=1は文字通りiに1を代入してるわけだが、このiという変数が何回ループを繰り返したか数えるためのカウンターになる
次の10は、終了条件で、iがこの値を超えたらループを終了して次に移ることになる
最後の3は、1ループごとにカウンターをいくつ進めるかの数値
ちなみにこの最後の値は省略可能で、for(i=1,10)と省略すれば自動的に1に設定されることになっている
つまりこの例の場合・・・
1から始まって、3ずつ増えていくカウンターが、10を越えたらループ終了、というわけだ
まだ若干わかりにくいと思うので今回は上の例を使って実際にどんな処理になるのか順を追って説明しようと思う
まずは1ループ目
最初に1度だけ()の中がチェックされて、ここでループのカウンターが設定される
()の中の1個目の式でカウンターを初期化する
つまりこの時点でカウンターiの中身は1
i = 1
2個目の値、10がループ終了の条件なので、
i <= 10
が真である間ループが繰り返されることになる
カウンターの増え幅は3個目の値、3なので、iの値は1ループごとに3ずつ増えていく、ということになる
とりあえずループの中身を1回実行する
aの初期値は0でa=a+1だから、この時点で
i = 1, a = 1
ここでカウンターが増える
いくつ増えるかっつーと最初に設定したとおり3ずつ増えるのでi=i+3で
i = 4, a = 1
ここで条件をチェック、まだi<=10は真なのでループは続行
i = 4, a = 2
そんでまたカウンターが増える
i = 7, a = 2
また条件チェック、まだi<=10は真なのでもう1ループ
i = 7, a = 3
カウンターを増やす
i = 10,a = 3
条件チェック、i<=10は真のままなのでループ続行
i = 10,a = 4
カウンターを増やす
i = 13,a = 4
条件チェック、i<=10が偽になったのでループは終了となる
・・・とまぁこんな感じなわけだがおわかりいただけただろうか・・・
正直上手く説明できた自信が一切ないわけだがw(死滅
ん〜・・・まいいや(滅殺

汎用for文
汎用for文の使い道は主に、[1]から順にきちんと要素が並べられたテーブルの、テーブル内全ての要素に対して一括して何か処理をしたいときに使われる
ぶっちゃけこれはもう半ば定型文になってて
for i,v in ipairs(テーブル名) do
  処理
end
でテーブル内の全部の要素に対してdo〜end間の処理を実行することができる
その場合、iには要素番号、vには要素の中身が[1]から順に1ループごとにどんどん入ってくるから、後はそのiなりvなりを使って処理内容を組んでやればいいわけだ
ホムAI組む上でも多分数値用よりこの汎用forのほうがよく使うと思う
これを使いたいってのがあって、テーブルを初期化するときは、
t={20, 54, 2, 104, 31, 76,(ry}
って感じにちゃんと先頭から順番に値が入るようにすることが多かったりする
まぁ多分実際組んでると慣れてきたらこの方法しか使わなくなるんじゃないかなw
でないと自分でわかりにくくなるだけだし・・・w
ちなみに汎用数値用に関わらずfor文も入れ子にできるよん
このとき注意するのが、例えば
t = {}
u = {}
a = {}
for i,v in ipairs(t) do
  for i,v in ipairs(u) do
    a[i] = v
  end
end
とかやっちゃうと、このiとかvって外のループの奴?中のループの奴?ってなっちゃうから、
t = {}
u = {}
a = {}
for i,v in ipairs(t) do
  for j,w in ipairs(u)do
    a[j] = v
  end
end
とか、カウンターに使う文字を中と外で変えてあげれば混乱しなくて済むってこと
数値用の場合もこれは同じことね

break文

もしwhileやらrepeatやらforやらでのループを途中で抜けたい時に使うのがbreak文
まぁまずは使い方を見てみよう
例として、コインを投げて表なら勝ち、裏なら負けのルールで5回やるとしよう
つまりこの場合、5回やりきらなくても3回勝つか負けるかした時点で勝ち越しか負け越しが決まるわけだ
ここで、勝ち越しか負け越しか判定するプログラムを作るならこんな感じになるはずだ
win  = 0 --勝ち数カウンター
lose = 0 --負け数カウンター
for(i = 1, 5)do --5回繰り返し
  (ry) --コインを投げて
  if(表)then --表なら
    win = win + 1   --勝ち数をカウント
  else       --裏なら
    lose = lose + 1 --負け数をカウント
  end
end
(ry) --勝敗表示とかいろいろ
さてこれで、winかlose、どちらかが3になった時点で、ループが途中だとしても勝敗は決してしまうわけだからそれより先のループはやっても無駄なわけだ
だったらどっちか3になった時点でループを終了して後の処理をやっちゃってもいいわけじゃん?
こういうときにbreak文が使われるわけです
win  = 0 --勝ち数カウンター
lose = 0 --負け数カウンター
for(i = 1, 5)do --5回繰り返し
  (ry) --コインを投げて
  if(表)then --表なら
    win = win + 1   --勝ち数をカウント
    if(win == 3)then --カウンターが3まで回ったら
      break --ループ終了
    end
  else       --裏なら
    lose = lose + 1 --負け数をカウント
    if(lose == 3)then --カウンターが3まで回ったら
      break --ループ終了
    end
  end
end
(ry) --勝敗表示とかいろいろ
こうすればカウントを増やした後に、勝ち3回目か負け3回目の時点でif文の中に入ってbreak文にたどり着くことになる
そしてbreak文にたどり着いた時点で5回ループしなくてもループを脱出して次の(ry)の処理に進んでいく
・・・とまぁ、こんな感じで基本的にbreak文はif文とセットにして、「ループの中で特定の条件が満たされたら、そのループを強制的に離脱する」って使い方をされることになるね
さてここで、さっきの入れ子のfor文、ここで
t = {}
u = {}
a = {}
for i,v in ipairs(t) do
  for j,w in ipairs(u)do
    a[j] = v
    if(条件)then
      break
    end
  end
end
とした場合はどうなるかというと、条件を満たせばbreakが書かれた場所から一番内側にあるループが終了する
つまりこの例だとj,wを使ってるほうのループが終了するから、i,v側が次のループに突入して、改めてj,wのループも値を変えてやり直されることになるね
ここで、
t = {}
u = {}
a = {}
for i,v in ipairs(t) do
  for j,w in ipairs(u)do
    a[j] = v
  end

  if(条件)then
    break
  end
end
となっていれば、条件が満たされた時終了するのは外側のi,vのループだからループ全体が終了して次の処理に進むことになる
とまぁ、break文に関してはこんなところかなぁ

終わっ・・・た・・・(´・ω:;.:...
うっわ、半日かかってるし・・・
こんなもんに半日アホだな俺orz
まぁ・・・おや・・・s・・・(´:;.:...

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Wiki内検索

Menu

test

管理人/副管理人のみ編集できます