Minecraftとタートルと僕スマートフォン用の表示で見るMinecraftとタートルと僕PCゲームMinecraftのMOD「ComputerCraft」の情報を集めたニッチなブログです。こちらのページは更新が滞っており、情報が古くなりつつあります。新しいCC情報サイトをはじめましたので、もしよければご参照ください。今後ともよろしくお願い申し上げます。なに?そういえば、これまで何度も使っているのに説明をしていませんでした。良い機会ですのでLuaにおける変数の取り扱いについて説明しましょう。最初に重要な項目だけ先に提示します。他のプログラム言語を使ったことがあるなら、Luaにおける以下の3つのルールを覚えるだけで問題ありません。用語については、「ローカル」→「プライベート」、「グローバル」→「パブリック」と置き換えて読んでもらってもかまいません。(ルール1)Luaは、local宣言しない変数はすべてグローバル変数として扱います。(ルール2)local宣言した変数のみローカル変数となります。endのような、for構文中のカウンタ用変数はローカル変数です。(ルール4)関数定義(function)も変数と同じように、頭にlocalでローカル関数になります。いう人だけ読んでください。ルール1)local宣言なしはすべてグローバル変数これまで変数を定義(以降、わかりやすさのために「使用宣言」と書きます)するときに、次のように行ってきたと思います。10という数字を入れたグローバル変数xをこれから使いますよ!このグローバル変数xは、使用宣言した場所より後ろであれば、プログラムのどこであろうと参照できます。変数を参照できる範囲のことをスコープと呼ぶのですが、グローバル変数のスコープは「変数宣言以降の場所であればすべて」です。便利で強力なのですが、使い方を間違えると影響範囲が大きいので大変な目にあいます。「さっき他の関数で計算した数値をこっちでも流用したいな・・・・・・。これをやりすぎるとプログラムの構造が滅茶苦茶になります。ルール2)local宣言するとローカル変数ローカル変数とはlocalと頭につけることで、その変数はローカル変数として使用できるようになります。10という数字を入れたローカル変数yをこれから使いますよ!ローカル変数はスコープが限定された、謙虚な変数です。ここだけで使いたい、他の場所で使う予定はないし、というときに使う変数だと思ってそう間違いではありません。スコープが限定されているので、ローカル変数を使うときには常にスコープを意識しながらプログラムを書くことになります。場合によっては、スコープの制限によりプログラム全体を再構築しなくてはならないことも。それにより、見通しの良い、全体的にモジュール化された、バグの少ないプログラムが作られるのです。ローカル変数のスコープは「変数使用宣言をしたブロックの中だけ」です。end」などのブロック中でローカル変数を使用宣言すると、その宣言の直後の行からそのブロックの末端にある「end」の直前までそのローカル変数を参照することができます。以下のプログラム例だと、ローカル変数xのスコープは関数hevo()の中だけなので、ブロックの外であるmain部分でこのxを参照しようとしても失敗します。そのためこのプログラムはエラーでとまります。xはprint関数から見て存在しない変数なので、xの値は存在しないよ、nilだよ、とみなされます。print(x)はprint(nil)を実行することになります。結果もnilを返すことになり、プログラム作成者の意図と異なった結果になってしまいます。変数xが参照できない。print(nil)を実行するなお仮に、以下のようにxの頭のlocalをはずしてグローバル変数として使用宣言すると、このプログラムは「とりあえず」動きます。このような、「グローバル変数をサブ関数の中で宣言。実際に使用するのはそのサブ関数の外」という使い方はバグの元であり、まったくお勧めしません。プログラムが大規模になるほど、xがどこで宣言され、どこで使われているか把握するのが難しくなるからです。場合によっては、意図しない場所で間違えて上書きしたり参照してしまう可能性すらあります。ファイルの先頭でローカル変数宣言することについて最も外側の(一番上の)ブロックを考えると、それはプログラムファイルの頭から終わりまでというブロックになります。言い換えると、ファイル中で、なんらかのブロックに囲まれていない地(じ)の部分がトップレベルのブロックになります。外側(トップレベル)のブロックでローカル変数を使用宣言すると、それ以降に記述するブロックはすべてトップレベルブロックの内側(下位)となるので、どこであろうとそのローカル変数を参照できることになります。そのため、「グローバル変数のスコープ」と「ファイルの先頭で宣言したローカル変数のスコープ」は、ほぼ*1同じです。以下のプログラムはその実例で、正常に動きます。mainhevo2()これは、プログラム中からグローバル変数を完全に根絶やしにできることを意味します。意味しますが、別にグローバル変数の完全排除を推奨しているわけではないのでご注意ください。あなたは、グローバル変数を使ってもいいし、使わなくてもいい。以下のfor文は、printを計3回繰り返すわけですが、1回ごとの実行において利用される「変数i」は、1回ごとに生成&廃棄される、個別のローカル変数であることにご注意ください。print(i)endつまりこのfor文は、実質的に以下のプログラムとほぼ等価です。print(i)endルール4)関数にもローカル・グローバルがある以下のように、ブロックの中でローカル(プライベート)関数を定義することができます。myPrint("12345")end隠蔽化は、オブジェクト指向プログラミングにおいては重要なテーマですね。ローカル・グローバルを使い分けて、見通しの良いプログラムを普段から心がけると後々幸せになれます。いえ小規模なプログラムにおいて、関数のローカル・グローバルは、変数のときほど神経質になる必要はありません。問題になるのは大規模なプログラムを書くとき、あるいは他のプログラムから呼び出されるAPIを書くときです。このようなときには、ローカルとグローバルの区別を厳密に意識しましょう。APIの書き方については、そのうちしっかり時間をかけて説明したいと思います。結局、ローカル変数とグローバル変数をどう使い分ければいいの?使い方の原則はあります。そのプログラマーのポリシーしだいです。原則論グローバル変数は強力で影響範囲が大きいので、使用は最小限にすること。最小限とは、「そのグローバル変数がどこで定義し何のために使うものなのかを完全に把握できる数だけ」使うことです。グローバル変数の個数が多すぎてこれは何のための変数だっけ?などと思うようなプログラムは、そもそも設計が間違えています。プログラマーによっては、「グローバル変数はバグの元」と言って、ひとつも使わない場合もあります*2。たくさんのプログラマが参加する大規模プロジェクトでは、グローバル変数は完全に管理されており、上司の許可なしに勝手にグローバル変数を増やすと怒られます。そのため、「localつけないと基本はグローバル変数」という仕様になっているLuaは大規模プログラムを書くのに向かないことがわかりますね。まあLuaは、もともと組み込み用途で設計された言語だから当然といえば当然なのですが。僕の変数使用ポリシーグローバル変数は、プログラムのconfigパラメータや定数でしか使いません。定数は大文字で強調した変数名にしてあります。例外として、args={...}やmon=peripheral.wrap("top")のようなよくある定型文でグローバル変数を使うことがあります*3。他はほとんどすべてローカル変数です。※ひとつの独立したプログラムではなく、例としてソースコードの断片を掲載するときには、localを省略することが多々あります。※そして何事も例外があるということで、手を抜いて短時間で作ったプログラムにはしれっとlocalつけ忘れがあるかもしれません。その場合はご容赦ください。頭にlocalをつけずに、てきとーに使いまくっていると後で泣きを見ることになります。使うにしても、定数とか設定用パラメータくらいに使用を留めておくのが無難です。いうご指摘は真摯に受け止めさせていただきます。これもひとえに、私の書きたいことを思いつくままに書くというこのサイトのポリシーによるもので、今後も改善する予定はございませんのでご了承ください。次回以降も、テーマに沿いつつもそれに関連したことを思いつくまま、脱線しながら書き散らかしていくつもりです。このサイトを開始してから数ヶ月、読みづらい私の文章にお付き合いいただきありがとうございました。今後もこの調子で続けていくつもりですので、よろしくお願いします。それでは来年も良いお年を。*1:ほぼの理由は、Luaが組み込み用途で作られた言語であることに由来します。Luaプログラムを呼び出す外側のプログラム(たとえばJavaなど)から、そのLuaプログラムのグローバル変数に直接アクセスできます。APIとして他のプログラムから呼び出す場合にも挙動に違いがありますが、ここでは説明を省略します。*2:たとえばJava言語は、厳密な意味で、グローバル変数はありません。*3:本当はこれもlocal宣言しておくべきなんですけどね。今後は極力気をつけます。はてなブログPro「Minecraft」「ComputerCraft」について知りたいならば「お勧めコンテンツ」の『ComputerCraftとは』をご覧下さい。