◆はじめに
夢と絶望の魔境、プログラミングの世界へようこそ!ぼくはプロレタリアプログラマー人間だよ!気さくにプロプロ人って呼んで欲しいな💖
今日は君たち先天性下等生物どもにプログラミングの基礎を教えるよ!絶対に逃がさんぞ!君もプログラマーという名の家畜になり下がろう!🐷🐷🐷🐷🐷🐷🐷🐷
…はい。えーじゃあやっていこう。
ある言語についてじゃなくて、プログラミングするうえで登場する概念的なところの説明をしていく。初心者向けに。
語彙の英訳を記載しておく。エラメを読む力を養え。
◆値と変数
変数つかうよね。「値を入れる箱」とかいう教わり方をしたかもしれないな。
ネタバレすると、変数は箱じゃないです。変数です。
変数は変数でしかないから、何にも例えようがない。そんなもんでとりあえず「変数」がなんであるかっていうのは置いておいて、機能面からおさらいすることにしよう。
int hoge; hoge = 1; int fuga; fuga = hoge + 10;
この例を上から順次よむ。
- 変数「hoge」を作成
- 数値「1」を代入
- 変数「fuga」を作成
- hoge変数に「1」が入っている状態で、変数「fuga」に「hoge + 10」を代入している。
代入は「ぶち込む」ってこと。結果的にfugaは「11」になるだろう。
大多数のプログラミングの言語では、記号「=」に「代入演算子」の機能を割り当てている。右辺を「評価」した結果を左辺の変数に「代入」するのが代入演算子の機能だ。右辺左辺っていうのは「=」の右側と左側のこと。「評価」っていうのは計算のことだと思えよ。
変数に代入した、つまり、変数にぶち込んだ値を「変数名」により再利用することが出来る。また、「変」とつくように、変数に入っている値は変えることができる。つまり再度値を代入することができる。
という説明で、全部。意味がわかってもわからなくても取り敢えず読み進めろ。
変数を箱として捉える「代入」に対して、変数をラベル(なふだ)として捉える「束縛」という概念がある。値にその「ラベル」を貼り付けて、ラベルを使って値を読み出すという考え方だ。束縛と代入は概念的には違うんだが、「変数への値の代入は、値に変数名をラベリングするようなものだ」って考え方も許される。し、そのほうが頭に入りやすいような気がする。なぜなら「変数」のキモは「変数名」だからだ。箱が主役ではなく、箱についた名前、すなわちラベルが主役といえる。
※数学でいう代入、束縛とは意味が異なる。また、再代入、再束縛を許さない言語もある。
変数は英語で「variable」。「vary」が「変更」。「able」が「可能」。つまり「変更可能であるもの」といった意味だ。
プログラミングにおける代入は、英語で「assignment」あるいは「assign」。数学における代入は「substitute」だから意味が違う。
▼代入は関数
※意味を理解できなくていいのでサラーっと読み飛ばせ。
あとで関数の話をする。するんだが、今のうちに言っておくと「代入」の操作も関数です。さらに言えば加算も減算も関数。
1 + 1 ↓ Add(1, 1)
int x; x = 1; ↓ int x; Assign(x , 1); つまり x に 1 + 1 を代入するときは以下。 int x; Assign(x, Add(1, 1));
そんな感じ。代入も加算も関数なんだけど、読みづれぇから「=」とか「+」とか「-」で表現してもいいよってことになっている。
関数、たとえば「+」を対象の間に挟める記法を中置記法という。かたや、Add(x, y)みてぇに関数が先頭に来るのは前置記法(ポーランド記法)。後置記法(逆ポーランド記法)もある。
値と参照の話が後で出てくるんだけど、代入操作も関数であるわけだから、値の代入と参照の代入の2種類がある。どっちの代入であるかは言語に因って変わる。
理解できなくていい。いまは。
▼右辺に代入先が出てくるやつ
これ
int hoge; hoge = 1; hoge = hoge + 1;
このコードは初心者にとってスッと理解できるものではないだろう。
こういうのは束縛のほうが理解しやすい。流れをたどってみよう。
- 「hoge = 1」の右辺が評価され、結果は「1」だった。
- 値「1」に「hoge」変数が束縛される。
- 「hoge = hoge + 1」の右辺が評価され、結果は「2」だった。
- 値「2」に「hoge」変数が束縛される。
結果としてhogeは2であると結論付けることができる。変数は箱ではないし、変数としか言いようがないんですよ。新しい概念だ。
整数値がすべて含まれている集合に「1」も「2」も含まれるだろ?そこに対して「hoge」というラベルを貼ったり剥がしたりしているようなものだ。実際は数値に上限があるから「すべての整数値」とはいかないが。
▼式
式。プログラミングにおける式は、英訳すると「expression」。これも数学で言う式とは違っていて、数学の式は「equation」だ。
さきほどから「評価」という言葉が登場していたが、評価されていたものはそりゃあもう「式」だ。
hoge = 1 + 1;
の右辺は式。
hoge = 1;
の右辺も式。
hoge = hoge + 1;
の右辺も式です。
式が評価されると最終的に「値」になる。値は変数に代入することができる。(式そのものを代入する言語もあるけど。)
お前らの好きな「return」の右側に書かれているものも式だな。
じゃあ例えば、以下の例はどのように動作するだろうか。「hoge」と「fuga」に入る値は?
int hoge; int fuga; fuga = hoge = 1;
これは、言語によって異なる。そもそも許されない構文かもしれない。
理解のキモは「hoge = 1」自体が式であるか否かだろう。例えばJavaScriptとかだと式として扱われる。「hoge = 1」という式を評価した結果、値「1」が算出される。また、変数「hoge」に「1」が代入される。「hoge = 1」が「1」と評価されるわけだから、「fuga」には「1」が代入されてしまう。
気持ちわる。JavaScript気持ちわる。
キモい理由として、式のくせに「状態」を変化させてしまうということだ。
「状態」についてはあとで解説する。
▼変数の宣言、定義
大抵の言語では、「変数つかうよ」と宣言しなければそれを使うことができない。宣言はつまり準備と読み替えてもいい。宣言は英語で言うと「declaration」「declare」だ。
さっきもあったけど「int hoge;」とかが宣言の例だな。「int」っていうのは「Integer」つまり「整数」の意味で、これはC言語系の変数宣言といっていいかもしれない。言語によって変数宣言のルールは異なる。「整数かは知らんがとにかく変数」といった風に用途を定めずに変数を宣言することも可能かもしれない。
「定義」という言葉は変数には使わない。なぜなら、変数は中身が変わるからだ。中身が変わるんじゃ「義を定める」ことができないだろう。
ちなみに定義の英訳は「define」。「def」と省略されたりする。
▼アドレス
計算機の動作の話を少しだけする。
メモリってあんじゃん。パソコンに載ってるやつ。「メモリ8GB」みたいな。プログラムがなんらかの計算を行うとき、そのメモリ(メインメモリ)を使って変数を作ってます。
メモリの「記憶領域」が8GBあるわな。そのうちの例えば32bitをプログラム(のプロセス)が「使うわ。俺以外さわるんじゃねぇぞ。」と予約する。
その予約した32bitには、メインメモリ上での位置がある。その位置の事をアドレスと呼んでいる。説明してるとキリがないから白目剥きながら納得しろ。
予約した32bitの、その開始位置アドレスに名前を付けて、プログラムからアクセスできるようにする。それが変数の宣言と言える。「開始位置」って言ってんのは、つまりデータは一本の長い紙テープみたいなものだからだ。8GBのうちの32bitの範囲は1列に隙間なく並んでいる。だから開始位置と長さが分かればデータを取り出すことができるだろ?
(プログラミング言語の実装にもよるんだが、概ね“厳密には違う”感じのことを言ってる。ごめん。いずれは、お前さんの書いたコードがどういう流れを経てどのように実行されるのかってのを勉強してみればいいと思う。)
変数名とアドレスが紐づいていて、そのアドレスに格納されているデータを読みだして使用している。そういうこと。
「メモリの記憶域を予約すること」は「確保」と呼称される。英語では「allocation」「alloc」。「memory allocation」を「malloc」と省略したりしてるな。確保したメモリ領域を手放すことは「解放」と呼ぶ。
▼初期化
変数の初期化。英語では「initialize」あるいは「init」。
変数を宣言しながら値を代入することができる。
int hoge = 1;
このときの「1」は式だし、より厳密には「初期化子」と呼ばれる。勘違いしている人が多くて困るんだが、これは「はつき ばけこ」と読む。
嘘です。
変数を宣言しながら代入できるのは便利だし、また別の意味を持っていたりする。
さきほどメモリ領域の確保の話をしたが、その確保したメモリ領域の内容って一体全体どうなっているだろうか。
これは、まず分からない。何が入っているか不明だ。だから初期化子を指定してやる必要がある。具体例を見てみよう。
int hoge; int fuga = 0; fuga = hoge;
このとき、fugaの値はどうなる?それは誰にも分らないんですね。変数「hoge」のメモリ領域を確保したときに、たまたまそこに入っていた値になってしまうからだ。だから初期化子をつかって値を確定しておいたほうがよかろう。
C言語の話をしているからそんな感じだ。だが、大抵の言語に於いて整数値の変数を宣言した時は、自動的に「0」で変数を初期化してくれる。しかし言語に依存するものだ。初期化せずに変数を宣言するってのはそういうリスクがつきまとう。気になったら動作確認しておけ。
▼型
変数には、「入る値」「入らない値」がある。どういうことだろうか。なんでも入ってくれないと困るわ。訴えるぞ。
「型システム」という仕組みがある。例えば「string」「char」「int」「long」「配列」とかみたことあるだろ。それらが型だ。
型は言語によって取り扱いが異なるんだが、Cを参考に見てみよう。
int hoge = 1;
これはよかろう。整数の変数に1が入った。
char hoge = 1;
これはまずいね。一文字を表現する「char」に整数値をいれようとしている。ルール違反だろう。文字なら「1」ではなく「’1’」だ。
こんなルールがある理由には、メモリが絡んでくる。また、コードの可読性にも関わってくる。
まずメモリ。「char」を1バイトの情報量だとする。かたや「int」は4バイトだったとするわな。変数宣言で「char」を作ったら、1バイトのメモリ領域が確保される。だが、そこに整数値である4バイトを入れようとしたらもう爆発することうけあい。
そうでしょ?
だから、「型」がどれくらいの情報量なのかっていうのを定めて、それぞれの型には定められた値しか入れられないようにしたってわけだ。「long」っていう変数が「int」よりも大きい整数を入れられるのは、「long」変数が確保しているメモリ領域が「int」よりも大きいから。
「じゃあ全部longでよくね?」って話になるんだが、昔はそんなにメモリサイズが大きくなかったんです。だから節約の為にintとlongの使い分けが必要だった。今の時代だとメモリなんざメガどころかギガで積まれているが、それでも不要にメモリを確保するのは破廉恥なのでやめよう。
可読性の話をすると、型は定まっていたほうが読みやすいし、誤動作を起こしづらくて安全。なんでって、じゃあ例えば変数の型が定まっていない世界があったとする。ある変数を見たときに、その変数に入っている値が処理のタイミングによって文字だったり数値だったりって変化してしまう。その変数を処理するルートを2パターン書かないといけなくなる。書き忘れたら実行するときエラーになって怒られる。困る。もっとあるけど、簡単に言えばそういうこと。
でも型の扱いが緩い言語もあるよ。
▼「アドレス型」の変数
ちょっとおさらい。
変数にはアドレスが紐づいている。そのアドレスからデータを読み出すことができる。つまり変数からデータを読み出すことができる。
これはいいだろう。
そして例えばその「データ」として、「アドレス」を扱うことができるんじゃねぇか。
できます。アドレスもデータなのだから。そのアドレスは「ポインタ変数」と呼ばれるやーつで取りまわす。詳しく掘っていくとC言語講座になっちまうから説明しない。
例え話をする。
お前は「1GBのポエム」を書いた。それを誰かに共有したい。共有する方法は二個ある。
- ポエムのデータを相手のPCに送る
- ポエムのURLを相手に教える
データをそのまま受け渡すと、つまり相手のPCにコピーが増えることになる。コピーするのには時間と記憶領域が必要になるだろう。しかし、URLを連携できればコピーを作る必要はなくなる。
ポエムから卑猥な文言を取り除かなければいけなくなったとき、送ってしまったコピーまでは更新できないだろ?だけどURLによる共有であれば内容は随時更新される。
例えばURLに落ちているポエムを送信先の相手が編集できるのであれば、その内容をお前も確認できる。二人は同じものを見ている。
データのアドレスを渡すか、データ自体をコピーして渡すか。ポインタは、そういった「データの受け渡し」で活用される。関数の話を後でするからそこでも解説する。
▼ぬるぽ
ガッ!
JavaでいうところのNull Pointer Exceptionこと「ぬるぽ」は「参照によるデータの受け渡し」が絡んだ時に発生する。
※Javaではポインタは隠蔽されている。
参照を格納する変数を宣言したとき、「Nullポインタ」という特殊なポインタが設定される。これは「どこも指示してないポインタ」だ。その参照の中身を弄ろうとしても、無理。メモリ領域が確保されていないわけだし。それで怒られる。Nullについては一本記事が書けちゃうアレなのでここでは割愛する。
ちなみに「Null」を「ヌル」と読むのはドイツで、アメリカでは「ナル」だ。音楽の世界で「A」を「アー」と読むように、プログラミングではドイツ読みの「ヌル」が浸透している。現場によって協調して合わせろ。
余談だが俺は「重複」を「ちょうふく」と読む。
▼文字列、配列
文字列ワカリマスカ?たとえば「string」とかってやつだな。stringは「紐」とか「一列に連なったもの」を意味する。一文字であるchar、すなわちcharacterが連なった結果「string」となるわけだ。
メモリをガン意識しなければならないC言語では「string」型は存在しない。文字列は文字の配列、すなわち「charの配列」として扱ってやる必要がある。
取り敢えず文字列は置いておいて配列を見ていこうかイェイイェイ。
int hoge[100]; hoge[0] = 1; hoge[1] = hoge[0] + 10;
int型が100個入るメモリ領域を確保したいなら「int hoge[100]」となる。
これねー。解り辛いよね。何で変数名の右っかわに要素数がつくわけ?型の右側に要素数を付けさせてくれよ。よみにくい。C言語が悪い。
まぁいいや。とにかく「hoge」という「intが100個入る配列」な変数が宣言された。ヤッタネ。
要素にアクセスする方法はご覧の通りですわ。変数名の右側に「[0]」だの「[1]」だのといった「添え字」を指定してやることで取り出す。100個分の要素があって、一番初めの要素に振られている番号は「0」。最後の要素は「99」になるわけです。言語によって違うけどな。1からスタートの言語もあろう。
配列の要素といっても、その実は変数だ。変数のように使え。
今回は100個の要素を宣言したわけだが、例えば要素「hoge[200]」に値を書き込もうとするとエラーになる。セグフォってやつだな。OSくんが「おめーそのメモリ領域確保してねぇじゃん。ダッサ。参照する権利ねぇから。帰って泣きながらマスでもかいてろ童貞人間。」と注意してくれるんですね。
配列には「可変長配列」という種類がある。要素数が可変であるか否か。つまり、hoge[100]の配列を宣言した後で、要素数のみをhoge[1000]やらhoge[10]やらに変更できれば可変長だ。
モダンな言語だと可変長配列に類するものが用意されているだろう。ListとかCollectionとか呼ばれている。
配列は英語で「array」。
・二次元配列
matrix。 説明しない。
VBAの変数宣言は「Dim」でなされる。これは、配列(次元)の宣言「Dimention」からの名残だ。
・文字列
さっきも言ったけど文字の配列です。
実装について説明しようかと思ったけどC言語講座になるからやめた。今までの話を理解できたんなら難しいこっちゃないし、自分で調べなね。
▼定数
変数は中身が変わる。逆に定数は中身が定まっている。
以下の目的で使用される。
- 値が変わると困る
- 「値は変わらないよ」と明示して可読性をあげる
初期値から値を上書きすることができない変数と思えばいい。
英語では「constant」「const」。また、「静的」を意味する「static」だとか、「読むだけ。書いちゃダメ。」を意味する「readonly」という風に表現されたりする。
▼スコープ
変数やら定数やらには「スコープ」つまり通用範囲がある。通用範囲を超えた場所では、その変数は使用できない。「そんな変数ないよ?変数宣言してね?」とか言われてしまう。
関数が絡むんで、そこで解説する。
▼理解度チェック
Q:黒柳徹子の一番好きな動物はカンガルーである。〇か×か。
A:×。黒柳徹子が愛している動物はパンダ。幼少期に父からアメリカ土産としてもらったパンダのぬいぐるみをきっかけとして、パンダの研究をするようになった。
Q:コアラの握力は何Kgか。
およそ15kg。1tもの握力があるという噂が過去ながれていたが、デマ。
◆関数
ある処理を分離したいときに「関数」とかいうものを作るだろう。あるいはファンクションと呼ばれたりサブルーチンとかメソッド、プロシージャ?もうワケがわからないよ(◕‿‿◕)
お前らがボンヤリと思っているそれは多分「サブルーチン」だろうな。
言語によって呼び方がいろいろ異なるんで混乱するね。また、「関数」は数学から拝借した語彙であるので、「数学での意味」も混在していて辛い。
▼サブルーチン
「処理を切り出したもの」がサブルーチンだ。メインルーチンがあって、その副処理であるからサブルーチン。ルーチンは「手順」「手続き」って意味。
#include <stdio.h>
int hoge(int fuga);
int main(void){
int piyo = hoge(1);
printf("%d\r\n", piyo);
}
int hoge(int fuga){
return fuga + 1;
}
hogeがサブルーチンですな。mainがメインルーチン。
※Cではサブルーチンを関数、ファンクションと呼ぶ。けど、とりあえず今はサブルーチンと呼ばせていただく。
hogeは、メインルーチンからintを貰っている。そして、貰ったintを参照するために「fuga」と名前を付けているな。このfugaは「引数(ひきすう)」と呼ばれる。昔は「いんすう」と呼んでいたらしい。
英語では「arguments」「arg」「args」。
サブルーチンでなんやかんや計算した結果を「return」、つまりメインルーチンに返却している。返却された値は、「戻り値」と呼ばれる。今回の例では戻り値を変数piyoに受け取っている。
じゃあ、hogeを「消費税を計算するサブルーチン」にしてみる。
#include <stdio.h>
double hoge(int price);
int main(void){
double piyo = hoge(100);
printf("%f\r\n", piyo);
}
double hoge(int price){
return price * 1.08;
}
hogeはメインルーチンから「100」を引数に受け取り、消費税率0.08をアレしてメインルーチンに返却している。
このサブルーチンは便利ですね。なぜなら、消費税を計算したくなった時に何回でも呼ぶことが出来るから。
サブルーチンを実行することは「コールする」「呼ぶ」って感じに表現される。
今回の例だと恩恵が少ないが、もっと複雑な計算になったときに大きな効果を発揮するだろう。
また、来月(2019/10/1)から消費税率が10%になる。さらに軽減税率なんていう複雑な処理も必要になるそうだ。そのときもhogeのなかにガリガリと処理を書くだけで済む。影響範囲をサブルーチンに閉じ込めることが出来る。
今回は名前を「hoge」としたが、そういう名前を付けてはいけない。変数でもサブルーチンでも、名前が適切に付けられていないとコードが読みづらくなる。残業する羽目になる。
サブルーチンには動詞から始まる名前を付けてあげよう。
今回で言うと「税込み価格を計算する」という意味だから「calcTaxIncludedPrice」的な名前になるかもね(適当)
▼余談:なまえ
一連の処理に名前を付けることが出来るのもサブルーチンの強みだ。処理を区切って名前を付ければ、何をしているのかが一目瞭然になる。ただ、名前がおかしいとコードは読めなくなる。何度でも言うぞ。コーディングするうえで名前を付ける場面になったときは、ちゃんと考えて付けろ。プログラマーに英語力が必要となる理由は、第一に名前を付けたり名前を読んだりするためだ。英語の勉強をしろ。
良い名前を付けることが出来ればコードコメントなんて不要だ。コードそのものが処理内容を示すようになるから。
「良いコードだけど名前がおかしい」はまず存在し得ない。良い名前が付いていることが良いコードであることの必要条件だから。そして逆に「名前は良いけど悪いコード」もない。なぜなら、良い名前は良い変数、良いサブルーチンにしかつけることが出来ないからだ。
「このサブルーチン名前つけづらいな」と思ったんなら、九分九厘やりたいことがおかしい。筋の悪いことをしようとしている。
▼関数、ファンクション
関数を英語で言うとFunctionです。だからそれぞれ同じものです。だけど、ちょっと表現がフワッとしてる。文脈により意味が異なる場合がある。
古くは「函数」と呼ばれるもので、「数(すう)を内包した数」を意味する。数学の用語ですね。数学で言う関数は以下のとおりに表現される。
y = f(x)
この「y」が関数。つまり「f(x)」が関数。数学だからっつって失禁して気絶する必要は無くて、じゃあ、fを例えば「価格xを引数にとり、税込み価格を返却する手続き」と考えればよい。
fの式を見てみると、こうなる。
f(x) = x * 1.08
じゃあ、関数「hoge(price)」にしてみる
hoge(price) = price * 1.08
こういうこと。
本来、独立変数priceを含めた「hoge(price)」まででもって「関数」と呼ぶんだけど、プログラミングでは「関数hoge」みたいに呼ぶ。俺もそう呼ぶ。
hogeが関数でpriceが引数。数学で言うと違うんだけど、プログラミングではそういうこと。
※用語の使い方がアレかも。数学ガチ勢の人の突っ込みを待ってます。
▼サブルーチンと関数の厳密な違い
「サブルーチンと関数、同じもの疑惑」がお前らの頭の中に渦巻いたかもしれんが、違うっちゃ違う。厳密には違う。コナンと新一くらい違う。
以下のサブルーチンを見てみそ。
#include <stdio.h>
double hoge(int price);
int main(void){
double piyo = hoge(100);
printf("%f\r\n", piyo);
}
double hoge(int price){
double r = price * 1.08;
printf("%f\r\n", r);
return r;
}
消費税を計算した後に「printf」してますね?このprintfに当たるものは数学の関数には存在していません。
じゃあ…もう…なんか関数とは呼べないような気がする。この気持ちわかるか?
もうちょっと分かりやすい例を見るために、まず「フィールド変数」について説明する。
▼フィールド変数
どうぞ
#include <stdio.h> void hoge(); int fuga = 0; int main(void){ hoge(); printf("%d\r\n", fuga); hoge(); printf("%d\r\n", fuga); } void hoge(){ fuga++; }
※voidは「戻り値無し」という意味。また、引数を取らないルーチンの引数書くとこにもvoidを挟みこんでいいよ。
mainの実行結果は以下の通り。
1 2
フィールド変数である「fuga」は、文字通りフィールド、つまり「場」に配置された変数だ。変数fugaにアクセスできる人はフィールドにいる人。メインルーチンもサブルーチンもフィールドに配置されているから、それぞれフィールド変数にアクセスすることができる。
mainでhogeをコールすると、フィールド変数fugaに1がプラスされるな。その後、mainでprintfすると1がプラスされた結果が標準出力に表示されるはずだ。
変数の届く範囲を「スコープ」と呼ぶ。フィールド変数のスコープはフィールド全域。
たとえばmainルーチン内で変数「piyo」を宣言したとき、hogeルーチンからpiyoにアクセスすることは叶わない。piyoのスコープはmainルーチン内である。
▼手順と関数
ということを学んだうえで、関数。
(プログラミングにおける)(狭い意味での)関数というものを簡単に説明すると、以下の感じになる。
えー?関数ー? えーとなんかぁ、引数をぉ、何個か渡すとぉ、マジ新しい値返ってきてぇ、チョーウケる。引数変えると戻り値も変わるしぃ、ミキもマジ爆笑してた。
だとすれば、さっきのhogeは関数だっただろうか?何も貰わず、何も返していないのだ。つまり、サブルーチンの動作パターンとして以下を挙げることが出来るだろう。
- 引数が必要であるか否か
- 戻り値があるか否か
別の視点から
- フィールド変数の値を読むか否か
- フィールド変数を書き換えるか否か
こんな感じ。
そして狭い意味での「関数」は以下の性質を持つ
- 引数を受け取る
- 戻り値を返す
- フィールド変数に一切アクセスしない
逆に、サブルーチンはなんでもあり。
引数によって戻り値が一意に決定されるものが関数であるから、引数を必要としない関数があればそれは同じ値しか返さない。だとすればそれはつまり定数だ。
以上。こんな説明で伝わるかね。
▽余談:それってファンクションか
慣例的に、フィールド変数とか書き換えまくってるサブルーチンであっても関数と呼ばれている。プログラミングにおける関数とは、概ねサブルーチンを指す。C言語のせいです。
数学的な文脈を継承している関数は、プログラミング界隈では「純粋な関数」と呼ばれたりする。
▽余談:パラメータ
「引数」と「パラメータ」は違う。言い分けている人はいないし、俺も言い分けてないから全部「引数」でいいんだけど、一応。
処理していく中でサブルーチンに渡されるものが「引数」。
サブルーチンが「こういう値受け取るぜ」って宣言してるのがパラメータ。和訳すると「仮引数」。
つまり「サブルーチンの引数を増やす」ことはできない。「パラメータを増やす」ことはできる。パラメータはサブルーチンに定義されるもの。引数はサブルーチンに渡すもの。
これも数学で言う「パラメータ」「媒介変数」とは違う。よのなかどうなっとるんかのう。
よくわからねぇんなら後はググれ。
▼参照渡しと値渡し
「値渡し」という手法で今までサブルーチンの例を書いていた。つまり、ポインタが渡っているんじゃないってこと。実のところ、変数の値のコピーが生成されて、それがサブルーチンに受け渡っていたんです。
だからサブルーチン内で引数をいくら書き換えても、mainで宣言された変数の中身が書き変わることは無かった。
そいで、変数のアドレスがサブルーチンに受け渡ったんだとしたら、サブルーチンのほうでもmainで宣言された変数の値を書き換えることができますわな。
それが「参照渡し」。
※「ポインタ渡し」と「参照渡し」がある。それぞれ違うものだから注意。CやらC++を使うんじゃ無かったらポインタ渡しについて勉強する必要はとりあえず無い。説明は省略する。
Cには参照渡しがないからC#で説明させていただく。
※Console.WriteLineで標準出力に表示するもんだと思え。
using System; class Test { static void Main() { int fuga = 1; Console.WriteLine(fuga); Hoge(ref fuga); Console.WriteLine(fuga); } static void Hoge(ref int fuga) { fuga = 10; } }
int型の変数「fuga」が参照渡しされている。C#では「ref」キーワードが引数につくことで「これは参照渡しっすよ」ということを明示することが可能だ。
「参照」は英語で「reference」「refer」「ref」。例えば「Null参照」は「Null Reference」。
fugaはまずmainルーチンに於いて1で初期化され、Hogeサブルーチンが呼ばれることにより10で上書きされる。参照渡し!
サブルーチンの引数が値として渡るか、あるいは参照として渡るか。これは言語によって異なる。新しい言語を触る時は確認してみてもいいかもしれん。
そして、「引数で貰った値を書き換える」というパターンのサブルーチンが存在することを理解いただけただろうか。
▽余談:フィールド変数に触んな。引数に触んな。
フィールド変数はどっからでも値読めるし、値書き換えられるし、便利。
じゃないんですね。罠ですアレは。変数というものは、スコープが広ければ広いほど収拾がつかなくなる。コードを読んでいる時に、フィールド変数の値を常に頭で記憶していなければ動きを追うことが出来ない。
実感がわかないかもしれんが、とにかくフィールド変数をなるべく使わないようにコードを組み立てること。なるべく純粋な関数になるようにサブルーチンを書くこと。
フィールド変数、グローバル変数を宣言することを「汚染」、「pollution」と表現することがある。それほどまでに、広いスコープを持つ変数は嫌われるのだ。
そして、引数に触ることも許さん。なぜなら「引数を書き換えるサブルーチン」と「引数を書き換えないサブルーチン」が混在すると、サブルーチンを呼んだときに引数に何が起きるのか分からなくなってしまうからだ。そんな状態で迂闊にサブルーチンを呼ぶと、引数が上書きされて困ったりする。
「フィールド変数が書き換わる」「引数が書き換わる」ことは「サブルーチンの副作用」と呼称される。
この記事では一貫してサブルーチンという単語を使ってるもんでアレなんだが、つまり「副作用をもつ関数」「副作用を持つメソッド」っていう表現が一般的だ。サブルーチンなんて言い方はイマドキ誰もしない。
ついでに説明するが、フィールド変数のことを「状態」「statement」という風に呼んだりもする。コードを読むときは、その「状態」が少なければ少ないほど読みやすくなる。さっきも言ったけど、状態があるプログラムを読もうとしたとき、その実行時の状態をずっと暗記していないと何が起きるかわからなくなっちゃうから。
だから状態にアクセスしない関数を作れ。状態を読むのも禁止!呼び出し元から受け取れ。
場合によっては、ルーチン内で宣言された変数を状態と呼んだりもする。視点がどこかによる。例えばルーチン内の「式」が、ルーチン内の変数を書き換えたんなら「式が状態を書き換えた」となる。ルーチンがフィールド変数を書き換えたんなら「ルーチンが状態を書き換えた」となる。
▼コルーチン
省略!
▼プロシージャ
処理の手続きっすよつまりは。ルーチンだと思っていい。
▼メソッド
オブジェクト指向言語において、インスタンスになされる命令、メッセージを受ける口。あるいはクラスに用意された静的サブルーチン。あるいはサブルーチン。
とりあえず説明しないし、理解できなくていいよ。オブジェクト指向言語だったら大体全部メソッドです(暴論)
▼算出
オブジェクト指向言語において、あるオブジェクトが例えば「誕生日」という状態を持っていたとする。
「誕生日」と「現在日時」という状態が分かれば、「年齢」を導き出すことができるだろう。
そういう類の、つまり「引数を受け取らず、状態だけを読んで、状態を書き換えずに戻り値を返す」というパターンのメソッドも考えられる。Webとかで画面から呼ばれるメソッドを考えるときに、そのパターンが頭にあると便利。
強みは「清らかな気持ちでViewにバインドできること」。
分らんかもしれんが、きっといつかUI構築するときに理解できるはずだ。
さも「年齢」という値があるかのように扱うことが出来るから便利。メソッド名も動詞スタートじゃなくて名詞になる。C#だとそれは「expression-bodiedでget-onlyなプロパティ」と呼称される。
※expression-bodiedである必要はないが。
「状態を書き換える」「状態を読みだす」は分離しておかなければならない。辛い目にあうから。
Vue.jsというWebフレームワークでは、そういうのを「算出プロパティ」と呼んでいる。けど算出プロパティで状態を書き換えたりできちゃうんですよね。困るわ。
◆以上
お疲れ様。理解していただけただろうか。
プログラムって大体こんな感じです。
解り辛いとことかあったら「あのアレがわかんねぇよ殺すぞ」ってコメントしてね。生きてたら追記しておくわ。
じゃあな!
コメントを残す