Vertical Composition with Blue and White by Piet Mondrian, 1936. https://www.wikiart.org/en/piet-mondrian/vertical-composition-with-blue-and-white-1936
はじめに
この章では、変数について学びます。 変数はプログラミングにおける最初の関門(しかも難関)ではなかろうかと私は思っています。 プログラミングを学び始めたばかりの人には難易度の高い「変数」ですが、 皆さんに意地悪をするために、このような機能が用意されている訳ではありません。
「変数」という概念を使わなければ、実現できないことが存在したり、 もしくは変数を使った方が便利、もしくは理解しやすい場合があるため、 変数という概念・機能が存在するわけです。
とはいうものの、少々難解な概念ではありますので、 その必要性や便利になる様子を軸にこの章では説明していきます。
面倒な状況を体験する
変数の必要性や「ありがたさ」を実感するためには、 変数を使わないと面倒な状況を体験するのが良いと思います。 ここでは以下に示す図のような絵を、 プログラムにより描くことを通じて、あえて「面倒な状況」を体験してみましょう。
この図形を描くプログラムは以下の通りです:
// vertical comp-position ver.0
background(250,245,230);
size(500,500);
noStroke();
fill(0,80,255); // blue
rect(450,0,50,100);
strokeWeight(10);
stroke(0,0,0);
line(450,0,450,500); // vertical
line(450,100,500,100); // horizontal
上のプログラムで描かれた作品は、 青色の四角形の位置が少々右すぎるように感じます(あくまでも私の感覚ですが)。 もう少し、四角形の配置場所を少し左側に移動させてみましょう。 もちろん、単に移動させるだけではなく、幅も増やさないといけません。 それも増やすようにしてみます。 例えば、rect 関数の第 1 引数を 450 から 300 に減らし、 第 3 引数を 50 から 120 に増やして様子を見てみましょう。 プログラムは次のようになります。
// vertical comp-position ver.1
background(250,245,230);
size(500,500);
noStroke();
fill(0,80,255); // blue
rect(350,0,120,100); // <-- change
strokeWeight(10);
stroke(0,0,0);
line(450,0,450,500); // vertical
line(450,100,500,100); // horizontal
このプログラムを実行すると、次のような作品が得られます:
…残念ながら、絵が壊れてしまいました。 良く考えてみれば、キャンバスのサイズは size 関数にて 横幅 500 ピクセルと指定しています。 なので、右端いっぱいまでの四角形を配置するのであれば、 四角形の幅は四角形の左上の座標の x 値と関係するはずです。 左上の座標の x 値が 350 であるならば、 四角形 の横幅は 150(= 500 − 350) としなければなりません。
また、縦線を描画している line(450,0,450,500); の引数も修正が必要です。 今回、位置を修正した青色の四角形の左上の座標と重なるように描画するためには、 この line 関 数の第 1 引数と第 3 引数の値を 450 から 350 に変更する必要があります。 同様に、水平線の左端の座標についても修正が必要です。 具体的には、line(450,100,500,100); と書いていたものを、 第 1 引数を 450 から 350 に 変更し、line(350,100,500,100); と書き換える必要があります。
このように、修正を施したプログラムリストとその実行結果を以下に示します:
// vertical comp-position ver.2
background(250,245,230);
size(500,500);
noStroke();
fill(0,80,255); // blue
rect(350,0,500-350,100);
strokeWeight(10);
stroke(0,0,0);
line(350,0,350,500); // vertical
line(350,100,500,100); // horizontal
なかなか良い感じです。 これら関係する場所の数値を全て修正することで、壊れた絵がなんとか直りました。 一安心しつつ、もういとど生成された絵を眺めてみると、あと少し、 もうちょっとだけ縦線の位置を右にずらしてみたくなりました。
また、もう一度、これまでのようなプログラムの修正を行わなければなりません。 しかも、修正すべき場所を全て修正し、かつ、忘れることなく直していかないといけません。 何度もプログラム修正して確認、 という作業を延々と繰り返すこととなります。 これは非常に面倒です。
これは、なんとかならないものなのでしょうか? (…という気分になれば、このセクションは上手く機能した、ということになります)。
プログラムの構造
縦線の位置または青い四角形の左上の位置を、気軽に変更するため、 先に示したプログラムの構造をみてみます。 このプログラムは、縦線の位置および四角形の左上の頂点を x=350 の場所に 変更したものでした。 この 350 という数値に着目してプログラムリストを再度眺めてみると、 次のようなことが分かります。
最初に 350 という数値が出てくるのは、7 行目の rect 関数の引数です:
rect( 350 ,0,500- 350 ,0);
// ^^^ ^^^
ここでは、分かりやすいように x 座標の値 350 に関する部分の下に コメントで印を付けておきました。
同様に x 座標の値 350 に関連する部分を以下に示します:
line( 350 ,0, 350 ,500); // 11 行目
// ^^^ ^^^
line( 350 ,100,500,100); // 12 行目
// ^^^
x 座標の値 350 に関する場所を全て調べてみると、計 5 ヶ所あります。 つまり縦線の位置を、例えば 350 から 335 に変更する場合、 これら 5 ヶ所 全ての数値を変更しなければ、絵が壊れてしまうこととなります。 言い換えると、こんな簡単な絵を生成するプログラムであっても、 描画の位置を少し変更するためには 5 ヶ所の変更が必要であることを意味します。 もちろん、もっと複雑な絵を生成するプログラムであれば、 修正すべき場所は増える可能性が高くなります。
単に、描画位置を変更するだけなので、 これらの変更は自動的に更新されるようにできないものなのでしょうか? いちいち手動で関連する場所をひとつひとつ修正していくのは面倒です。 また、手作業で都度修正していくのでは、修正し忘れも発生しそうで心配です。
…とまあ、このような気持ちになったのであれば、 あなたには良いプログラムを書ける素質があります(多分)。 少々の面倒を受け入れることにより、 より大きな面倒を回避するという考えはプログラム開発においては結構重要な考えだと 私は思っています。
少し話が逸れました。話を元に戻します。 そもそも、縦線の位置を変更するたびに、 何故、プログラム中の 5 ヶ所を変更しなければならないのでしょうか? それは、これまでに示したプログラムリストが「具体的すぎる」からです。
具体的の反対は抽象的であり、具体的な情報を抽象的にするためには、 情報量を減らさなければなりません 。
情報量を減らすとは、どういう意味なのでしょうか? ここでは、まず情報を増やしてみることについて考え、 その後、反対に情報を減らす、ということについて考えてみます。
一般的に、情報を増やすとより具体的になります。 例えば、犬という情報に「秋田犬」とか「柴犬」と いう情報を付加すると、 より具体的な犬の犬種について述べることとなります。 一方、「犬」という概念から、犬という具体的な情報を減らすと、 例えば「哺乳類」とか「脊椎動物」とか「動物」、「生物」という、 より抽象的な概念となります。
プログラムに戻って、もし、上に挙げた 5 ヶ所の部分を、 例えば以下のように記述できたらどうでしょうか?
rect( 縦線の x 座標の値 ,0, 500-縦線の x 座標の値 ,0 ); // 7 行目
line( 縦線の x 座標の値 ,0, 縦線の x 座標の値 , 500); // 11 行目
line( 縦線の x 座標の値 ,100, 500, 100); // 12 行目
もし、このような書き方をしていれば、 任意の場所に縦線を描く場合でも修正は要らなさそうです。 つまり、もし、次のようなプログラムが許されるのであれば、 どのような場所に縦線を描画しようとも対応できそうです:
// pseudo vertical comp-position ver.3
background(250,245,230);
size(500,500);
noStroke();
fill(0,80,255); // blue
rect(縦線の x 座標の値,0, 500-縦線の x 座標の値,0);
strokeWeight(10); stroke(0,0,0);
// draw a vertical line
line(縦線の x 座標の値,0, 縦線のx 座標の値,500);
// draw a horizontal line
line(縦線の x 座標の値,100, 500,100);
実は、上のプログラムはこのままでは動作しません。 なぜ動作しないのでしょうか? これはコンピュータの側に立って、実際の処理について考えてみると分かります。 例えば、7 行目に「rect(縦線の x 座標の値,0, 500-縦線の x 座標の 値,100);」 というプログラム片があります。 rect は四角形を描く関数でしたが、さて、 キャンバスのどこに四角形を描けば良いのでしょうか? 「縦線の x 座標の値」とありますが、「具体的に」どの場所に描けばよいのでしょう?
Processing に限らず、プログラムの実行に関しては、 具体的な値というものが求められます。 上のプログラムにおける rect 関数においては、 「縦線の x 座標の値」とされている部分の「具体的な値」が分からないので、 このプログラムは動かない、という結論になります。
「だから 350 という値を書いていたのに」という気持ちは十分に理解できます。 しかし、それでは rect や line 関数の引数として、情報が具体的すぎるのです。 関数の引数に具体的な値を書くのではなく、 例えば、「縦線の x 座標の値は 350 とする」という記述が、 プログラム中になされていたらどうでしょうか? この方法であれば、rect や line 関数の引数として、 具体的な値を使わず、かつ、直線や長方形を具体的に画面のどの場所に描けばよいか、 ということも明らかとなります。 プログラムで書くとすると、こんな感じになることでしょう:
// pseudo vertical comp-position ver.3
background(250,245,230);
size(500,500);
noStroke();
fill(0,80,255); // blue縦線の
x 座標の値は 350 とする// <--- 具体的な値の情報!
rect縦線の ( x 座標の値,0, 縦線の500- x 座標の値,100);
strokeWeight(10);
stroke(0,0,0);
// draw a vertical line
line縦線の ( x 座標の値,0, 縦線のx 座標の値,500);
// draw a horizontal line
line縦線の ( x 座標の値,100, 500,100);
このプログラムは 8 行目にて、具体的な x 座標の値が与えられています。 なので、理屈としては動作するハズです。 実際、プログラムの流れやプログラムに必要な情報については十分なのですが、 Processing では我々が日常使用している言葉(自然言語)は理解されませんので、 「縦線の x 座標の値は 350 とする」と書かれていても処理できません。
これをどのように Processing が理解できる形で記述するのかについて、 次のセクションで説明します。
縦線の位置変更に対応するプログラム
上のプログラムリストで問題だったのは、 日本語で書かれている「縦線の x 座標 の値は 350 とする」という部分でした。 そもそも Processing のエディタ (プログラムを入力する部分)に 日本語を入力しても文字化けしてしまうので、 日本語を使わずにこの情報を記述しなければなりません。
「縦線の x 座標の値」を例えば px とする場合、 「縦線の x 座標の値は 350 とする」という文章は 「px は 350 とする」と書き換えることができます。 なぜ px なのか?と疑問を持った方もいるかもしれません。 結論から言ってしまえば、私が適当に決めたから、です。 アルファベットまたはアンダーバーと呼ばれる記号で始まる文字列であれば 何でも良いのですが、position x の意味ぐらいとして px と名付けました。
「px は 350 とする」という処理は px=350 と書きます。 数学の場合、px = 350 という記述は px が 350 と「等しい」という意味でもありますが、 Processing においては等号(’=’ イコール)の記号は「代入」を意味します。
a=1 ならば a を 1 とし(代入し)、 以後 a と書かれているところは 1 と書かれているのと同じとなります。 なので、
px=350;
rect(px,0, 500-px,100);
というプログラムは、
rect(350,0, 500-350,100);
と同じ動作となります。
では、プログラムでも px=350; と書けば良いかというと、 もう少し記述が必要です。 単に px=350 とか書かれても「そもそも px って何よ?」という状態になります。
まあ、そうはならない言語もあるのですが、 このシリーズで扱っている Java という言語では「そもそも px って何?」 という状況になってしまいます。
px は値 — ここでは整数 — を表している記号であることを示す必要があります。 そのためには int px; という記述をします。 int という単語は整数を意味する integer から来ています。
int px; と書けば、 px という表記は整数を表すものだとコンピュータに伝えることができます。 このように、px とは何者だ?という情報をコンピュータに与えることを「宣言」と言います。 コンピュータに一度伝えてしまえば(宣言してしまえば)、 あとは px に値を割り当てることができるよう になりますので、 px=350; などと書けるようになります。
実際のコードで示すと、
int px;
px=350;
といった感じです。
もちろん、350 という値に限らず 349 という値でも構いません。
int px;
px=349;
宣言と当時に値を代入する(割り当てる)ことも可能です。 その場合は、
int px=350;
などと書きます。
ここまでの知識で、 px という表現を用いて抽象的な描画プログラムを記述できるようになりました。 px を用いて、プログラムを次のように修正すれば、 実際に動作するプログラムとなります:
// vertical comp-position ver.4
background(250,245,230);
size(500,500);
noStroke();
fill(0,80,255); // blue
int px=350; // <--- 具体的な値の情報!
rect(px,0, 500-px,100);
strokeWeight(10);
stroke(0,0,0);
line(px, 0, px, 500); // draw a vertical line
line(px,100, 500,100); // draw a horizontal line
このプログラムは縦線の描画位置変更に対応したプログラムです。 なので、8 行目の px の値を ー 例えばもっと極端に ー 100 へと変更してみましょう:
// vertical comp-position ver.5
background(250,245,230);
size(500,500);
noStroke();
fill(0,80,255); // blue
int px=100; // <--- 350 から100 に変更
rect(px,0, 500-px,100); 11
strokeWeight(10);
stroke(0,0,0);
line(px, 0, px, 500); // draw a vertical line
line(px,100, 500,100); // draw a horizontal line
変数とは何か
上のプログラムリストを使うと、あまり手間をかけずに ー つまり px に代入する値を変更するのみで ー 生成される作品を (ある程度ですが)自由に変更できるようになりました。 px へ代入する値を変化させても、プログラムの 10 行目以降は修正する必要がありません。
これは一体どういうことなのでしょうか。 10 行目以降のプログラムは、ある程度の抽象化が実施されており、 例えば、縦線は x の座標値が px である縦線を描くよう抽象的に line(px,0, px,500) とプログラムされています。
抽象的な処理として書かれているので、 px に 350 や 100 などの整数値が代入されていても、 破綻することなく処理が行われます。
さて、抽象的に書かれたプログラムにとっては、px というのは一体なんなのでしょうか? あるときは 350 という値(整数値)であったり、 またあるときは 100 という値になったりします。
10 行目以降のプログラムにとっては、 px は変化する数値、つまり変化する数に他なりません。 この変化する数 px のことを「変数」と呼びます。
int px; などとした宣言は「変数 px を宣言する」等と呼ばれたりします (int px=300; というプログラム片でも同様です)。
変数は複数あっても良い
ここまで px という変数について説明してきました。 抽象的に取り扱う量・値が複数ある場合には、 複数の変数を使うこととなります。
一般的なプログラミング言語では、複数の変数が使用可能です。 もちろん Processing でもそうです。
以下、横幅の位置を表す変数を追加し、 より自由に絵作りができるようなプログラムを示して、 この章を終わることとします。
横線の y 座標については、py という変数を用いることとします。 これは縦線の x 座標についての変数が px だったので、 それに類する名前ということで py としただけです。
横線の y 座標は、四角形の高さ(height)でもありますので、 h という変数名でも良いかもしれません。
もちろんこれ以外の、例えば zzz といった変数名でも動作します。 しかし、変数名はコンピュータのためではなく、 プログラムリストを読む我々人間のためにあるものです。 そのため、できるだけ実体を示す名前をべきです (プログラムリストが読みやすくなります)。
変数 px と px を用いて、 実際に縦線と横線の位置を抽象化したプログラムリストを以下に示します:
// a study for vertical comp-position blue and white
background(250,245,230);
size(500,500);
noStroke();
fill(0,80,255); // blue
// position cofiguration
int px=380;
int py=300;
rect(px,0, 500-px,py); // draw a rectangle.
strokeWeight(10);
stroke(0,0,0);
line(px, 0, px, 500); // draw a vertical line.
line(px,py, 500, py); // draw a horizontal line.
そして、このプログラムで生成される画像は、 以下のようになります:
コラム:変数名は大事(ヒトにとって)
他人のプログラムを読む(解読する・理解する)のは非常に大変です。 そのため、できるだけ分かりやすい名前を変数に付けるなど、 他者に対して優しいプログラムになるようにしましょう。 あなたのその優しさは、必ずあなたを救うことでしょう。
プログラミング界隈には「3 ヶ月前の自分は他人」という言葉があります。 プログラムを作っていると、過去の自作コードを利用する・参照する場合があります。 そのとき、たとえ自作のプログラムであっても、 3 ヶ月も経ってしまうと作った時の記憶は薄れ、 もはや他人が作ったプログラムのように感じてしまう、 自作のコードとはいえそれを読み解くためには他人のコードと同じけのコストがかかる ーという意味の言葉です。
なので、できるだけ変数の機能や実体を表す、 分かりやすい変数名をつけるようにしましょう。 「情けは人のためならず」はプログラミングの世界にも存在します。