見出し画像

Retro-gaming and so on

プログラミング初心者が引っかかりやすい事〜端末とprint命令とreturn命令

もうこのブログ、使いづらいんで、プログラミングネタの話を書くのはやめようと思ったんですが。
一応、書いておかなきゃならないネタを思いついたんで書いておきます。プログラミング初心者へ、の話題です。
どうも教えてgooを見てると、returnとprintを混同してるようなプログラミング初心者を見かけます。いや、これは別に不思議じゃない。恐らく・・・誰でも通る道でしょう。特に「インタプリタで初めてプログラミングを勉強しはじめた」層に顕著、です。いきなりコンパイラを使って、の人だとこういう混同はない。しかし、インタプリタは原則、「結果を表示してくれる」機能が含有されていて、開発だと凄くやりやすいんですが、殊更returnやprintの意味が「イマイチピンと来ない」事が起こります。ワタシもそうでした(笑)。しかも最悪な事に、「プログラミング初心者向け」と謳ってる書籍でも、作者が「何が初心者に既知の知識で、何がそうじゃないのか」把握してないんで、この辺丁寧に説明してる本ってまず無いんですよ。まあ、「プログラミング初心者向けの本」で本当に初心者向けの本って以上の理由でほぼ無いんですが。
しかも、今の若い人だとDOS窓使った、って事がほぼ無いんで、「printの意味」ってのが良く分かってないんですよね。っつーわけで、ちと説明しておこうと思います。

まずは実例から。大昔、Apple IIと言うパソコンがありました。世界初のパーソナルコンピュータです(実は違うんですが、まあいいでしょう)。


このApple IIと言うパソコン、電源を入れると上のような真っ黒な画面が出てきます。今で言うといきなりDOS窓が立ち上がるようなモンですね。ウィンドウもデスクトップもクソもない。当時(70年代〜80年代)のPCは基本的には全部そう、です。コンピュータとやり取りするのは全部この端末を介して、だったのです。
ついでに、Apple IIは8bitマシンだったんですが、この当時の8bit機ってのはOSなんてものは載ってなかったんですね。パソコン用OSってのはあったこたぁあったんですが、大体オプションでした。と言うのも、今で言うストレージ(HDDやSSDとか)が無かった。オペレーティングシステム、なんつってもオペレートする対象が無かったんですよ(笑)。当時はフロッピーですが、フロッピーもオプションだった。従ってOSもオプションだったのです。
じゃあ、この端末で表示されてるカーソル(Apple IIだと])とか何なんだ、と。答えはBASIC言語です。この当時の8bitパソコンは大体、電源を入れるとすぐBASIC言語が立ち上がる。言い換えると、この当時のパソコンはBASIC言語を使うためのマシンだったんです。隔世の感がありますね(笑)。
さて、Apple II。つまり、この状態でプログラミング言語に計算させる事が出来ます。試しに3-1を計算させてみましょう。


3-1を入力しましたが何も表示されませんね・・・・・・。失敗したか?
いやいや。実は見えないけど計算自体は行われてるんです。内部的には。しかし、表示はされない。何故なら表示しろって命令してないから、ですね。
計算結果を表示させるには次のように打ち込まないとならない。


これで結果を表示する事が出来ました。計算して、その結果を端末上に表示させたい場合は常にPRINT命令を使わないとならない、って事です。
さて、これで分かった事があります。実はBASIC言語に限らないんですが、他のプログラミング言語でも出力命令、と言うのは端末上に結果を表示する命令なんです。他のどこでもない。Windowsで言うとDOS窓に表示する為の命令が出力命令なんですね。
昔の・・・平たく言うと、金持ってる家で、パソコンを親にねだって買ってもらえた財力があるウチのガキは全員これを知っていました。反面、現代っ子でWindows使ってもDOS窓なんか使った事がないんだけど、プログラミング学び始めました、って子はこれを知らない。ここが決定的に違うんです。
こういう端末とのやり取りをする仕組みの名称を、カッコよく言うと標準入出力(Standard Input and Output)と言います。誰も使わんDOS窓関連の機能を標準と言うたぁ・・・って不満に思うかもしれませんが(笑)、そうなのです。まぁ、昔に決まった標準なんで、現在だと非標準だろ、ってツッコミはアリですが(笑)、まぁ、そういうわけで。
だから例えばPythonインタプリタ使ってて、printをインタプリタ上で表示する為の機能だ、って勘違いすると困った事になる。Pythonでもprint命令は端末で表示する為の機能です。決してPythonインタプリタ上で表示する為の命令じゃあありません。
例えば次のような単純なプログラムを書いてみる、とする。



Pythonインタプリタでこのプログラムを走らせると結果は確かに表示されます。



ところが、このプログラムを端末で走らせようとすると・・・・・・。



ご覧の通り、何も表示されません。Apple IIのPRINTが欠けた結果と同じですね。Pythonでもprint命令は端末に表示する為の命令です。決してPythonインタプリタ上で結果を表示させる為の命令ではないのです。従って、ソースは次のように書き換えないとなりません。



こうしておけば、端末上で結果がキチンと表示されます。


さて、ではPythonインタプリタ上では表示されるreturnとは一体何なんでしょうか。参考書なんかでも「値を返す」たぁ良く書かれていますが、考えてみるとその意味、ないしは具体的な動作に関しては触れられてる事はないです。このreturnも実はプログラミング初心者にとっては分かりづらい機能です。
returnって言っても一体何がreturnされているのか。単純にreturnは日本語だと「返却」と言ったような意味になりますが、コンピュータ上では、元々こういう意味です。
アセンブリ言語と言う非常に低レベルな言語がありまして。これでプログラムを書く場合、通常、メインルーチンとサブルーチン、と言われるモノを分けて書きます。メインルーチン、と言うのは、プログラムの本体で、CPUがプログラムを走らせる時呼び出されるモノですね。ただし、メインルーチンに計算内容を全部書くと非常にゴチャゴチャしたプログラムになってしまう。それを避ける為、メインルーチンに全部詰め込まず、分けた処理をメインルーチンから呼び出すようにプログラムを書いていくわけです。この、メインルーチンから呼び出される部分がサブルーチンです。サブルーチンで処理が終わった時、メインルーチン内で呼び出された部分(具体的にはメモリ上のアドレス)に帰るわけです。これをreturnと呼びます。これが実は由来なんです。
高級言語に於いて、このまま実装されてる、たぁ限らないんですが、この辺の事を抽象化してるのは間違いなく、単純に言うと、このサブルーチンにあたる部分がいわゆる関数です。従ってreturnを使った時点で関数内から制御が「戻る」あるいは「返る」んで返り値、とか戻り値、とか言う言い方が出てきたんですね。本来、呼び出されたアドレスへと帰るんで「帰り値」でもいいんですが、いずれにせよ、「計算結果」を持ったまま呼び出された部分へと制御が戻ります。
C言語はかなり低レベルな言語で、割にこの辺のアセンブリでお馴染みだった「言い回し」をそのまま言語の機能として取り込んでいます(main関数、とか言うのもそうです)。そしてC言語の影響を受けたプログラミング言語も多いんで、return文ってのがあっちこっちの言語に取り込まれてるんですね。
実際は、言語によっては関数にreturnと言う文を採用していない言語もあります。代表的なモノとしてはPascalなんかがあります。Pascalの関数は、関数名に結果を「代入する」と言うような約束になっています。

(* Pascalでの3乗を計算する関数の例 *)
function cube3 (p: integer): integer;
 begin
  cube3 := p * p * p
 end;

Lispなんかは、基本的に「計算が終われば値を必ず返す」ように設計されているので、これはアセンブリの話と一対一対応じゃないんですが、特にreturnが無くても値は必ず返ります。

;;; Common LIspでの3乗を計算する関数の例

(defun cube3 (p)
 (* p p p))

とまぁ、こういう風な変わった言語もあるんですが、アセンブリに影響を強く受けてるC言語だと、returnを使って表現するようになってるのです。

// C言語での3乗を計算する関数の例
int cube3 (int p) {
 return p * p * p
}

Pythonもこの辺の表現はCの影響下にありますね。

## Pythonでの3乗を計算する関数の例
def cube3 (p):
 return p * p * p

いずれにせよ、「制御を関数から手放す(返却する)」と言うのがreturnの意図するトコロなので、印字されるかされないか、ってのは関係がないのです。今の4例のうち、インタプリタがメジャーじゃない言語、つまり、PascalとC言語はこのままコンパイルして走らせても結果は印字されません。一方、LispもPythonも基本はインタプリタなんで、インタプリタ上で走らせる以上は結果を見る事が出来る。ただし、端末からこれらのプログラムを呼び出して走らせる場合、やはり「端末で表示せよ」と言う命令(Pythonだとprint、Common Lispだと例えばprinc)がないと端末上では表示されません。この辺「正統なプログラムはあくまで端末上でやり取りする」と言う歴史的経緯を考えてみると、開発段階でインタプリタを使ってても、最終的には「端末上で使うにはどうデザインすればイイんだろ?」と今でも強く意識してないといけない、と言う事です。まぁ、この考え方は昔は当たり前だったんですが、今のGUIの世界では必ずしも実用的な考え方ではないんですけどね(笑)。

余談ですが、昔、Common Lispを勉強し始めた際に、Lispインタプリタで出力命令を書くと結果が二重に表示されるのに驚いたモノです。


Common Lispのインタプリタの場合、印字結果の後、関数の返り値を必ず表示するような仕組みになってるので、このように結果が重複されて表示されたりするんですね。これも「端末で走るように」プログラムを書けば最終的には二重表示にはならないんですが、開発途中だと「返り値と出力は違うんだよ」と言う一種の警告として受け取れます。でも最初見た時は「なんじゃこりゃ?」とか思ったのは秘密です(笑)。僕も当時は返り値ってやっぱり良く分かってなかったんですよねぇ。
結果、皆通る道、なのです。
  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

最近の「プログラミング」カテゴリーもっと見る

最近の記事
バックナンバー
人気記事