見出し画像

Retro-gaming and so on

抽象化の話

前回、典型的なC言語脳の例を見てみた。
断っておくが、真のC言語エキスパートはああいった考え方はしない。少なくとも仕様書くらいは読んでる。しかし、そもそも「仕様が何かを知らずに」C言語"らしき"モノを使ってる人間の方が圧倒的に多いんだ。結果として真のC言語エキスパートの数はかなり少ない。
どうしてC言語脳の方が多いのか。それは圧倒的にプログラミング言語を学びたくない、と言う人の方が多いから、だ。

プログラミングをやってみたい、と言う人が「何の言語を学べばいい?」と言う質問を良くする。もちろん「なるべく学びやすい言語を知りたい」と言う事はあるだろう。しかし、結構な割合で「ずーっと使えるプログラミング言語を学びたい」と思ってる。何故なら本音ではプログラミング言語を学びたくないから、だ。1つ覚えたらそれで一生プログラミングをしていたい。もう「学習する」と言う面倒くさい事をしたくない。
こういう人たちは実はプログラミング自体は好きじゃないんだ。単に就職の為、とか金の為、にプログラミングをしてる/したい、って思ってるだけで、「プログラミング自体は好きじゃない」。だから新しくプログラミング言語を学ぶ事を極端に畏れる。
しかし、結論から言うと、「1つのプログラミング言語を覚えてずーっとそれを使い続ける」と言う事は不可能だ。大まかには10年毎に「トレンドが変わる」のは、今までの動向を見ていて間違いない、んだよ。貴方が今使ってる言語は10年後には「流行ってない」事を覚悟しておこう。今はPython3が人気だけど「10年後に同じように人気がある」かは誰にも分からないんだ。「言語には流行り廃りがある」し、「流行り廃りが無いと」実は困るんだよ。
「流行り廃りが無いと困る」と言うのはどういう事だろうか。1つは技術の進歩による。そして「ユーザーが使いたい」と思うソフトウェアへの要求度の上昇に従って、「よりそれに対してプログラミングしやすい」ツールが必要になってくるから、だ。よってそれに対して対応出来ないプログラミング言語は廃れて行かざるを得ないんだ。
プログラミング言語は設計者によって「拡張される事はあり得る」。でも例えばJavaなんかを見てると、どう見ても基本設計がマズいんで、拡張された「部分」がどーにも醜い、つまり「使いづらい」と言う事も出てくる。歪な拡張だとどうしようもなくなる、と。そうなると「そういう言語は捨てて」、新しく一から設計された、機能が豊富な「綺麗な言語」を使った方がマシだろう、ってなる(※1)。今現在、Javaを学ぶよかC#を学んだ方がマシだろう、ってのはそういう事だ。今だとまだJavaの資産が多いが、そのうちC#の「資産の量」が逆転する時が分水嶺になるだろう。
じゃあ、C言語はどうなんだろう。1972年に登場したこの言語、50年も経ってるけど「その時生まれたまんま」なんだろうか。そんなわきゃあない
国際標準規格(ISO)だと今までバグフィックスを考えなければ(実は「仕様のバグ」と言うのがある)、初版を含めると都合3回改訂されている。1990年、1999年、2011年、の計3回だ。そして今年「4度目の改訂」が控えている。C言語も「プログラミングしやすさを目指して」一応10年前後毎に進歩してるわけだ(※2)。
ただし、C言語脳は「プログラミングが特に好きではない」し、「新しいプログラミング言語を勉強したくない」為、当然、アップデートしたC言語の新仕様を学ぼうとはしない。1980年代に学んだC言語を「そのまま」現代でも使おうとするんだ。
確かに、マイクロソフトの処理系が全然C言語の仕様にキャッチアップしなかった、と言う不幸はあった。しかし、Visual Studio2019以降、もはや「WindowsでVisual StudioだとマトモなC言語を扱えない」と言った事は無くなってる。マイクロソフト環境でも「国際標準の」C言語は扱えるようになってるんだ。
しかし、C言語脳は「新しい仕様を学びたくない」からこういう事を知らないし知ろうともしない。いや、「個人的にどういう状態でプログラミングしてもそれは個人の勝手」だ。好きにすればいい。マジでそう思ってる。ただし、それは人に教えるモンじゃねーだろって事を言ってるんだ。
そして、いっつも言ってるが、「C言語はプログラミングの基礎だ」とバカな事を言って、色気を出して良く知りもしないPythonを教える、なんつー事をやる。「標準のC言語も知らない」のにどうして「Pythonを知ってる」と言えるんだ
結局、ある種の思い上がりなんだよ。C言語脳は「C言語はプログラミングの基礎」と言う益体もないバカな事を言って、「ANSI Cを覚えたら」それで学習をストップする。そしてそのANSI Cで学んだプログラミングに対する貧弱な知識でPythonを経由して害毒を撒き散らす。
繰り返すが、「一人でどのようにプログラミングするのか」に対しては好きにしていいと思っている。しかし「俺はC言語を知ってるから人にどんなプログラミング言語でも教えられる」なんつー事を考えてるならいい迷惑だ。それは単なる勘違いなんだよ

件のページはホント「典型的なC言語脳」で、止めときゃいいのに「Python入門」なんつーバカなページまで作っている。
以前、「Python入門書ではこんな本は買っちゃダメ」っつー例を挙げたけど、見事に引っかかってる(笑)。俺ってすげーな(笑)。
IDEを導入させようとするわ(笑)、




リストの「破壊的変更メソッド」も大々的に「使う前提」になっている。




典型的なC言語脳だ。特に「データの破壊的変更」に躊躇がない。
っつー事は「モダンなプログラミングの基礎」を全く知らんって事だ。「C言語がプログラミングの基礎」なんつーのは「モダンなプログラミング法を全く知らない」と言うのと同義ならしい。
結果、このサイトではグローバル変数とその破壊的変更方法に関して延々と語ってる(ゲッ!)



確かにPythonはスコープが色々とヘンな言語だ。
しかし、一般的には記述コストがかかるのは、言語設計者がその機能を使ってほしくないからだ。それ以外に理由はない。
記述コスト、と言うのは「何らかの機能を使う際、どう見ても余分な記述を必要とする」事だ。Pythonのglobal宣言は典型的な例だろう。
別の言語でも、例えばRust。「再代入可能な」変数にはmutキーワードを必ず書かなきゃならない。それはある意味、ペナルティ以外の何物でもない。
要は、Rust設計者は「変数を再代入するようなプログラムを書かないでくれ」と言ってるんだ。
同様に、「グローバル変数への再代入」をPython設計者は望ましい、とは考えていない。だからPythonではC言語で言うconstを付けた状態をグローバル変数のデフォルトとしてるんだ。
まぁ、グローバル変数使いまくりの破壊的変更ありまくりのC言語脳的プログラミングだと想像もつかんだろうがな(※3)。

とまぁ、Python入門もトンデモだ。読むのも苦痛だったんだけど、例えばこんな事を書いている。



ここでもC言語の仕様に目を通した事がない、ってのがハッキリ分かる。C言語にも真偽値はあるstdbool.hを#includeすればtrue、falseは使えるようになっている。




Python3.10以降ではmatch文が追加されたが、恐らく時期的にはそれ以前に書かれた記事だろうからツッコまん。そこは見逃しておいてやる。
問題は、ここでも書いた、「条件式」だ。

三項演算子はPythonにも用意されているんですよ。ただ、C言語などの他の言語と比べると、癖が強いので慣れないと使いづらいですよ。

ifの左側に真のときの値を書くというのが、かなり違和感を感じますね。どうしてこんな言語仕様にしたのでしょうかね。不思議です。

要は、「C言語の構文と違うからわかりにくい」っつってるだけ、だ。そしてC言語脳はC言語より機能が高度な言語だと理解が追いつかない
どうした。C言語はミニマルだから網羅的に学べる、んじゃなかったのか(笑)。
っつーか、一回網羅的の意味を調べた方がいいんじゃないか(笑)。
どうも、こうやって見てると、C言語を学ぶと脳にダメージを受けるんじゃないか、って気がする。明らかに理解力が低下してる
「C言語はプログラミングの基礎」っつってもこんなモンだ。殆ど脳障害レベルの理解度の低さになってる
そもそも、条件式は「三項演算子」的だが「三項演算子」そのものではない。3つ以上項が取れる、ってのも何度もこのブログで紹介している。
すなわち、

条件式が真のときの値 if 条件式 else 条件式が偽のときの値

と捉えるのが間違いで、

値1 if 条件1 else 値2 if 条件2 else 値3 if 条件3 else ...else 値n

と好きなだけ条件と値のコンビを追加出来るんだ。
それが

ifの左側に真のときの値を書くというのが、かなり違和感を感じますね。

そうしてる理由だ。
結局、「C言語での三項演算子」でしか見てないからこういうトンマな感想を抱く。
当然ながらPythonはC言語ではない。C言語と言う色眼鏡を外さないと、C言語のブツより遥かに強力な機能に対して

癖が強いので慣れないと使いづらい

なんつーバカな事を言い出すわけだ。
そもそも「今からPythonを学ぼう」と言う人間に余計な先入観を与える必要もないし、そしてこういう思考経路が「リスト内包表記が分かりづらい」と言う原因なんだ。
C言語が全てではない。ちと他の言語の学習に対して謙虚になれよ、ってマジで思う。
繰り返す。C言語脳はC言語より遥かに高度な機能を持つ言語に違和感を覚えて学ぼうとしないC言語はミニマムなんで、網羅的なプログラミング言語学習の基礎を提供しない
言った通りだろ?C言語を学ぶと他の言語の理解を邪魔するだけ、なんだって。「C言語のメリット」を唱えてた本人がこの体たらくだ。
言ってる事に全く信頼がおけない、と言う証明だ。
C言語は抽象度が低い抽象化が足りない。そういう低レベルから上を見上げても他の言語への理解が追っつかない。
ポール・グレアムが言ってた「ほげ言語のパラドックス」の実例がこれなんだ。

なお、件のページの「Python入門」では例によってprint書きまくりなコードばっか紹介してる、って事を付け加えておく。

さて、上でも書いた通り、「C言語は抽象度が低いし、抽象化が足りない」言語だ。
とまぁ、このブログでは「抽象度」「抽象化」っつー単語を割に簡単に使ってはいるんだけど、ひょっとしたら「抽象度」とか「抽象化」と言う言い回しを知らん人もいるかもしんない。いや、いる。確実に。
っつーか、場合によってはコンピュータ・サイエンスで「Lispを学んだ」層しか知らんかもしれん、って今気づいた(笑)。Lisp関係では良く、抽象化(Abstraction)って言い回しを使うんだよ。
よって今回は「抽象化」「抽象度」とは何なのか、って話をしてみようと思った。マル。

IT用語辞典ではこんな事を書いている。

抽象化とは、対象から細部や具体性を取り去り、本質的に重要な要素や、着目している側面のみを取り出して、一つの概念として定義すること。また、異なる複数の対象に共通する性質や要素を見出し、共通点を組み合わせて汎用的な概念を構成すること。

まぁ、ちと「説明」が難しいかもしんない。
しかし、プログラミング言語上では簡単だ。平たく言うと、

「一行で書いた部分がより多くの事柄を成し遂げて」

いるのなら抽象化が上手く行ってる、ないしは抽象化レベル(抽象度)が高い、と言う事だ。
言い換えると、「同じ事柄を行うのに」多量の行数のコードが必要になるのなら「抽象化が上手く行ってない」、ないしは抽象化レベル(抽象度)が低い、ってこった。
もう1つ、C言語脳が信じられない話を書こう。コンピュータに於いて、高級言語が「抽象度が高ければ高い程」ハードウェアが「実際に何をやってるのか」を隠蔽化する。具現と抽象は対義語で、ハードウェア上の「動作」が具現だとしたら抽象は「ハードウェアの動作を隠蔽してる」んだ。
これも当然で、ハードウェアに近ければ近いほど、その動作は「人間の日常」から程遠くなる。一方、高級言語の役目は「人間の日常」に近い辺りまでハードウェアの動作を隠しつつその動作と結果を「抽象化」する事だ。
これのお陰で、高級言語はマシン語やアセンブリより「分かりやすく」なってるわけだ(※4)。
ハッキリ言おう。高級言語は「ハードウェアの動作」を人間の目から隠蔽する為に存在する。言い換えると「ハードウェアがどう動作するか」考える必要がないようにするのが「高級言語の存在意義」なんだ。
つまり、C言語脳が言うような「コンピュータの動作を理解しなければダメ」と言う文言は基本的にはなんだ。あるのは「コンピュータの動作を理解しなければ使い物にならない」抽象化がヘタクソなプログラミング言語と「コンピュータの動作を上手い具合に隠蔽化した」抽象度が高いプログラミング言語があるだけ、だ。
言わずと知れたC言語は前者の言語だ、ってだけだ。

抽象化、っつーのは例えば「用語」のレベルでも出てくるんだよ。
コンピュータは単純にはCPU(中央演算処理装置)とメモリのコンビで成り立ってる。この辺までは誰でも知ってるだろう。
でだ。じゃあ、プログラミング言語に出てくる「配列」っつーのは一体何なのか。少なくともCPUとメモリのコンビ、では「配列」なんつーモノは出てこない。
日本語で「配列」と言う、ぶっちゃけちと見慣れない単語は何なのか。ちなみに英語ではArrayで、その意味は「整列したモノ」だ。実は英語では割にフツーの単語なんだよな。日本語は何でも難しく訳す(笑)。
いずれにせよ、Arrayは「何だか知らんが整然と並んだモノ」って意味だ。そしてC言語では、結論から言うと、Array = メモリそのもの、なんだけど、敢えて別の用語を使ってる。「何だったらメモリそのものでも良くね?」なんだがな。
どうしてだろ?1つは「抽象化」したいから、だ。要はプログラマに「メモリと言うハードウェアの存在」を知らせたくないから、だ。あるいは他言語から移動して来たプログラマに「貴方の使っていたプログラミング言語で言う配列と同じモノです」って勘違いさせたいから、だ。
通常、フツーのプログラミング言語では、コンピュータにあるメモリの一部を自在に利用させたい場合、メモリを利用しつつ、メモリそのものではない、様に見せかける為に加工してそれを配列、と称す。これも実装上での抽象化、と言える。
例えばPascalは、元々はJavaと同じく、仮想マシン上で動く実行形式を作るプログラミング言語だ。当然、仮想マシン自体はメモリ上で動く。言い換えるとPascalはコンピュータ上で動く仮想コンピュータ上で動く実行形式を生成するわけだ。
そうなると、Pascalで使ってる「配列」と言うのが、必ずしも実体としてそこにあるコンピュータのメモリを「そのまんまで」利用してると言う保証はない。まぁ、最終的には何にせよメモリ上で動くんだが、直接的にハードウェアを利用したプログラミングをさせないように、むしろ隠蔽してるわけだ。そのお陰で、仮想マシンさえ移植すればどのコンピュータ上でも動く、と言う特性を手に入れた。まんまJavaと同じだ(※5)。
これはPascalの話だったが、通常、「配列」と呼ばれるデータ形式が「メモリそのもの」である保証はない。メモリを利用するにしても「扱いやすいように」機能を追加して、直接ハードウェアを操作するような形式を「むしろ避けてる」場合も多いんだ。
繰り返す。通常、高級プログラミング言語は、メモリを「配列」として実装上抽象化してる。それは「剥き出しのメモリそのものを」扱うのがメンド臭いから、だ。
じゃあC言語みたいに、ハッキリ言えば「配列がない」プログラミング言語はどうなんだろうか。



これはこれで1つの意見だ、とは思う。ぶっちゃけデータ管理する、と言うのと剥き出しのメモリそのままで扱わなければならない、ってのは関係ねぇけどな
勘違いしないで欲しいのは、C言語の「配列」の抽象度が低いのにはそれなりの理由があるだろう、とは思ってる。どうしても速度を稼ぎたかった、ってのがあっただろうから、だ(本音を言うと、最初の実装で単なる手抜きを行っただけ、だとは思うが・笑)(※6)。
しかし、C言語にはC言語の「目的」があったように、他の言語には他の言語の「メモリを配列として抽象化する」理由がある、ってこった。それは相対的な話であって、C言語の設計方法が絶対に正しいと言う話じゃあない
要は、このテの「意見」っつーのは、言っちゃえば絶対にC言語が正しい、と言う根拠がない前提での奢りなんだよ。
言い換えると、C言語は(それなりの目的があったにせよ)単に「メモリの配列としての抽象化が不十分な言語」だと捉える事が出来る。
そしてメモリリークやらセグメンテーション・フォルトやら、一体何度同様な不具合を食らうのか。何を言おうと「間違いを起こしまくる」プログラミング言語であるのは間違いないし、要は「危険なプログラミング言語」ではあるんだ。
メモリを配列として抽象化・実装した言語の方が遥かに安全だ。「間違いが起こしづらい」からだ。

また、前にも書いたけど、「ポインタと言う概念を理解する」のと「C言語の難解なポインタ周りの文法を知る」ってのも全く別問題だ。むしろ、概念だけで言うと、ポインタの「意味」を理解する事はさして難しくない。誰でも理解出来る範囲だろう。
例えばメモリの100番値に「300」と言う数値が入ってる。メモリの200番地には「400」と言う数値が入ってる。メモリの300番地には「1」が入っていてメモリの400番地には「2」が入ってるとする。
そこで、「メモリの100番地が指し示している番地内の数値とメモリの200番地が指し示している番地内の数値を足せ」って言われたら、それは「300だ」とか思うおバカさんはそうそういないだろう。どう考えても答えは3だ。
単純にそういう「参照」を扱うのがポインタがやってる事で、この程度の事は誰でも理解出来るんだ。
そうじゃなくって、「C言語のポインタ周りの文法」がしっちゃかめっちゃかだ、っつーのが問題なだけ、だ。「ポインタの理解」と「C言語のポインタ周りのメチャクチャな文法を理解する」っつーのは全く関係ない。


上のお題をC言語で書いてみた例。実はポインタが分かりづらい理由の1つは、上のお題の文章のような状態だと「100%答えは分かる」んだけど、「ポインタ使用」では「メモリの番地」が隠蔽され、「自動でコンパイラがメモリの番地を割り当てる」んで意味が分かりづらくなる、と言うトコがある。
しかしながら、この「メモリ割当」を人手で行うと、教材程度のモノだったら「出来るかもしんない」が、多量のメモリ割当を人力で行うのが大変なんで、「コンパイラに任せてる」わけだ。
その辺が初心者相手としては悩ましくなる原因の1つだ。


Pascalでのポインタ使用の例。Pascalではポインタに纏わる記号は^(キャレット)。しかも、宣言時は前方から修飾し、使用時に、「参照先の中身を知りたい」場合は後方から修飾する、と言う「意味が違えば記述法が違う」と言う当たり前の事を行っている。
また、Pascalには動的変数、と言う概念があり、newと言う手続きを用いれば自動で「参照先」になるメモリを型通りに確保してくれて、あまり悩まずに使う事が可能だ。


上のPascalコードをCに翻訳した例。両者の違いがハッキリと分かるだろう。
*はポインタ関係で使う記号だが、宣言時と使用時の「参照先の中身が知りたい」場合の記述法に差がない。しかしこの2つは実は全く別の機能なんだ。前者は文字通り、ポインタ作成の為の記号だが、後者は「間接参照演算子」と言って、前者の*とは全くの別物だ。
この「全く違う役割」を同じ記法で行ってる、と言うのがCのポインタの分かりづらいトコで、明らかにPascalの方が分かりやすい。
そしてC言語には動的変数と言う概念がない。代わりと言っては何だが動的配列と言う概念があり、しかもそれを定義するmallocの書式が分かりづらい(※7)。
また、どうして配列なの?ってのもこの例じゃ分かりづらいだろう。
っつーより、C言語では「配列は配列を意味しない」事を思い出そう。Cでは配列は「剥き出しのメモリ」なんだ。
つまり、mallocでintサイズ一個分の「メモリ」を確保しろ、って言ってるのは、Pascalで言うnewと同じ効果があるって事で、要はそれは「配列」ではないんだ。
しかし、Pascalでnewで済むのにCのmallocのクソややこしい書き方の要求、ってのは単にめげるだけ、だ。これだけでPascalのポインタの方が扱いがラクだ、と言う証明になると思う。
なお、C言語にはbool値があるが、printfの書式指定子には表示方法が含まれていない(こういう「片手落ち」がまたC言語には多い)。結果、defineマクロを書いて表示を操作する、っちゅうクソメンド臭い事を行う羽目となってる。

さて、なんでいきなり「抽象化」の話を書いてるのか。
それは、プログラミング初心者に対して行う「説明」で「無駄だろ」って思ってる事があるから、だ。
僕が思うに、「二大無駄な説明」ってのがあって、1つ目はいつか書いた通り、フローチャートの記述を強制する事。もう1つもいつか書いたが、「変数を説明するのに箱のメタファを使う」事だ。
1つ目は、もうハッキリ言ってやる。「フローチャートを書く」なんぞ無駄だ
僕が見てきた経験上、「フローチャートを書けるようなヤツはいきなりプログラミング言語を使わせてもプログラムは書ける」んだ。逆にプログラミングが出来ないヤツは「フローチャートを書く練習をどんなにしても」それ自体も上達せんし、またフローチャート作成をどんなに頑張ってもプログラミングが出来るようにはならない
つまりだな、「プログラミングが出来ないヤツ」をあぶり出す為に「フローチャート」をテストとして使う事は可能かもしんない。FizzBuzzと同じだな。ある種「プログラミングの才能テスト」としては使えるだろう。
でも「フローチャートを書けるようにしてからプログラミングを学ぶ」っつーのは全くの無駄だ。そしてプログラミングが出来ないヤツはどんなにしてもそもそも「フローチャートが書けない」んだ。
こう書くと「フローチャートはプログラミングの基礎」って思い込んでるヤツがまた現れるだろうが、ホントに基礎だと思ってる?マジに基礎なら学校で「フローチャートの書き方」の訓練を最低でも半年はすべきだ。理想を言ったら1年はやるべきだろう。まさかフローチャートはプログラミングの基礎とか言いながら1週〜2週で終わるような事だとか思ってんの?
「簡単な事」を基礎とは言わない。むしろ基礎は把握する事自体が難しいんだ。小学生が「算数」をマスターするのに何年かかってる、と思ってるんだ?6年は伊達じゃねーぞ。
もし、「フローチャート"程度"なら授業1コマ〜2コマ程度で終わるだろう」とか考えてるなら、それは「簡単」ではあっても「基礎ではない」と言う事だ。当たりめぇだろ。
「簡単に終わる」程度なら基礎じゃない。よって省いてもO.K.だ。

ここにも「抽象化」がある。どうして昔、フローチャートが必要だったのか、と言うと構造化プログラミングじゃなかったから、だ。
昔はプログラムは命令から命令へと「ジャンプする」のがフツーで、このジャンプ元とジャンプ先を正確に把握する為にフローチャートがあった。goto塗れのコードだと、迷わない為の「指針」が必要だったんだよ(※8)。
しかし今はgoto及びジャンプ命令は条件分岐や繰り返し構文により抽象化された。構造化プログラミングではフローチャートはもはや必要ないんだ。

次に「変数の解説」だ。
例えばC言語脳的な「変数の説明」とかこんなカンジだ。




これは別にこの人が強烈なC言語脳だからこういう説明をしてる、ってワケではない。この人は「封筒」を使ってるけど、封筒の代わりに「箱」ってメタファも良く行われる。例えば高橋麻奈の「やさしいC」なんかでも似たような説明になっている。

 
でも良く考えてみよう。この説明ってヘンじゃないか?
C言語では変数はダイレクトにメモリの一部(セグメント等と呼称されたりするが)かもしんない。じゃあ何で「変数」なんて名前を付けたんだ。「メモリ」と言う名前もある、「セグメント」と言う概念もある。
だったらわざわざ「変数」なんて言わないで、最初っから「セグメント」と呼称すればいいじゃないか。
「変数とメモリの関連性」じゃなくって単に最初からメモリで押し通せばいいんだ。「変数」なんてヘンに名前を変えるからおかしな状態になるんじゃないか、と。変数xじゃなくってメモリの番地xって言えばいいんじゃないか。充分意味は通じるだろ。
ところが「どうして」ってC言語脳は考えない、んだ。

「変数」(variable)は単に数学用語の変数、と同じモノだ。「メモリ云々」と言うより「抽象化」して、より一般的な概念にしたかったから、だ。・・・まぁ、「数学知らねーよ」って人もいるかもしんないが、少なくとも現役の中学生ならそのまま理解出来るだろう。
言っちゃえば、コンピュータは元々「数値計算を行う為のモノ」で、Fortranからこっち、数学的処理をやるのが暫く主流だった。そして「計算を行いたい」人たちは別にコンピュータの専門家じゃなかったわけだ(数学・物理・化学の人間が使いたいケースが多かった)。
そんな中で、「メモリのセグメントが・・・」なんて事言っても意味不明だ。必要なのは「当時のユーザー向けの」一般化だったわけだ。そう、抽象化だ。上で書いてた通り、「対象から細部や具体性を取り去り、本質的に重要な要素や、着目している側面のみを取り出し」たわけだ。
要は「概念的には」数学用語を転用して抽象化した方がマシだっただけ、だ。
ところが、C言語脳って「わざわざ抽象化したのを」元に戻そうとすんだよな。
ちなみに、前にも書いたけど、Lisp系の入門書で「変数は箱で・・・」とか言う説明は見た事がない。Pascal系の入門書でもそうだ。どの本見ても「変数は数学で言う変数と同じ」と言う説明で事足りるんだよ。
繰り返すが変数が箱(とか件のC言語脳サイトでの封筒)なんつー説明が成り立つのはC言語だけ、なんだよ。
んで、もっと言っちゃうと、(ある意味諸悪の根源の・笑)K&Rを見ても「変数が箱」(あるいは封筒)だなんて説明は無い。

 
同書では、ハッキリ言って、「変数の定義」さえ無い。いきなりこんな事を書いている。

In C, all variables must be declared before they are used, usually at the beginning of the
function before any executable statements.

C言語では全ての変数は使われる前に、通常は関数の冒頭、つまり実行文の前で宣言されないとならない。(※10)

まぁ、この本は元々、FortranやPascal経験者に向けて書かれたモノで、「プログラミング初学者向け」じゃない。
しかし、本当に「箱の説明が必要」、つまり他言語と大幅に違えば「C言語での変数の意味」を解説せなアカンくなるんだけど、解説していない。
つまり、ここでも「変数」と言う概念は「抽象化」なされたモノで、他の言語の経験者がCにやってきてプログラミングしても「使える」と思ってるわけで、わざわざ「メモリが・・・」と説明しなくても良い、って判断になってんだよ。
これがC言語設計者達の「思惑」だ。分かる?
しかも具現として「変数はメモリ」だけ、と捉えると他言語では「余計な説明」に終始する。件のサイトでもPythonの変数の説明で、こんな不必要な説明を行っている



こんなん説明せんでも、Pythonでもやはり「数学で言う変数と同じ」で扱えるようになってんだ(※11)。

もう一度言うと、プログラミング言語に於いて、極端に言うと「具現」を扱わないとならないのはC言語(かあるいはそれ以下のアセンブリや機械語等の低レベル言語)だけ、だ。他のプログラミング言語では実は「抽象」を扱ってる。
つまり、抽象化レベルが高ければ高い程、「抽象的に捉えないと」ならなくなり、結果、具現で考えてる「C言語的発想」だと全く理解する事が出来なくなる。
繰り返すが「C言語はプログラミングの基礎」にはならない。C言語的発想だと「理解できない」機能の方がこの世のプログラミング言語では多いんだ。
「抽象」に慣れよう。プログラミングの世界にはアッチコッチに「抽象化されたモノ」が転がっている。それらを拒否する「C言語脳に」なるな。
今回はそういった話だ。

※1: この考え方から言うと、「50年の歴史がある」C言語は、「学ばない」C言語脳ばっか抱えてて、ハッキリ言って老害だらけなんで、「捨てた方がいい」段階に来てるのかもしんない。
現在、「より良い」「システムプログラミング向け言語」であるRustが現れた。C言語より機能が豊富で、ある意味これこそ「網羅的な」プログラミング言語だ。しかも新しく登場してるんで「若者をマウンティング目的で徘徊してる」老害Cプログラマは近づかない。いっそ「老害を切り離した」新しい環境に飛び込んでいった方がいいだろう。
Rustは既にLinuxカーネルの一部の開発に使われ始めたらしい。Linuxの創始者、リーナス・トーバルズは「LinuxのカーネルにC++を採用する」事を頑として認めなかった。徹底した「C++嫌い」としてある意味有名な人である。
しかし、RustのポテンシャルはCに匹敵する、と感じたようだ。
Rustも現段階ではPythonのように「仕様がない」言語だ。しかし、今後広範囲に使われていくとしたら、そのうち標準言語仕様も制定されるだろう。
ぶっちゃけた話、「Cを代替する"次の"言語」はなかなか出てこなかったんだけど(実はマイナーながらD言語、と言うプログラミング言語もあり、これも原則「Cを置き換える為に」開発されたが、結果あまり上手く行ってない)、Rustは現在「Cにトドメを刺す」最有力候補となっている。
なお、Rustが「Cにトドメを刺したにしても」資産としてのC言語は残り続けるだろう。多分今で言うCOBOLのような存在になっていくのではないか。

※2: しかし、「言語の標準の改訂」とはまた別に「デファクトスタンダード」、つまり「広く使われてる仕様/実装」と言うのがある場合がある。世界的な傾向で言うと、C言語のデファクトスタンダードはいまだC99、ってカンジらしい(断っておくがANSI Cではない)。
いずれにせよ、「国際標準」は現時点ではC17だが、「一番使われてるのは」C99、とちとギャップがあるのは事実だ。事実なんだけど、それは「標準Cを教えなくて良い」と言う理由にはならない。
なお、JIS C言語は、ISO(国際標準化機構)でかつて定義されてたC99なんで、そういう意味だとJIS(日本産業規格)での「C言語」は、世界的な傾向に合わせてる、とは言える。

※3: C言語の名誉の為に言っておくと、「UNIX上での望ましいCプログラミングスタイル」では、「プログラムはフィルタを作るように書け」だ。これも何度か言ってる。
要は「元データを弄らずに」加工して生成したデータを返すような関数を書いて欲しい、とデニス・リッチー達は思ってたんだ。これは今の「関数型プログラミング」のアイディアに近い。
従って、「本来の」C言語でのプログラミングでは「グローバル変数使いまくり」の「破壊的変更ありまくりの」プログラミングは推奨されていない。
その性質が変わらざるを得なかったのは、要は昔の「ミニコンより遥かに劣る」貧弱な環境であるパーソナルコンピュータのせいだ。とにかく今から考えると遥かに「小さなメモリ」を上手く使って「マトモに動く」プログラムを書くには、グローバル関数と破壊的変更に頼りまくらないとどうしようもなかったんだ。
結局、以前の記事でも書いたが、「C言語は生まれた場所と全く違う環境で想定外の使われ方をした」せいで、そういうUNIX的には「悪しきプログラミングスタイル」が蔓延するようになったわけだ。まさしく「アセンブリ言語」代わりだったんだ。
なお、組み込み環境だと、「昔のPCでのプログラミングスタイル」で書かなければならない事が多いらしい。しかしながら「組み込みでそうだから」と言ってそれを「プログラミング初学者」に強制するのはこれはやっぱヘンな話であって、2023年現在の「富豪の時代」のパソコンでは、いい加減MS-DOS的なプログラミングスタイルは戒めるべきだと思う。
また、極端に言うと、「小さくて効率の良い実行形式」を作る、つまり最適化作業自体はコンパイラの責任範疇であって、人間様がコンパイラの代わりに最適化を頑張る、ってのはどう考えてもおかしな話だ。
仮にそこまで「手作業での最適化」にこだわるのなら、「アセンブリ言語を直書き」した方が良好な結果になるだろう。昔のファミコン/スーファミ時代のゲームプログラマが頑張ってたように。

※4: ただし、実の事を言うと「抽象化が全ての事柄を分かりやすく」するか、っつーとそうではない、ってのも事実だ。逆に「抽象化し過ぎて良く分からん」結果になる事もある。
Schemeの継続は「脱出を抽象化してる」わけだが、結果初見では「なんだか良く分からん」機能になってしまった。Schemeの初版では古典的なLisp同様、脱出に関して言うとcatchthrowを持っていた。しかし、インタプリタの実装で「継続」と言う物を独立して取り出せるように「プログラミング出来てしまった」為、catchthrowは消えてしまい、その代わりcall-with-current-continuationと言う物が取り入れられたわけだ。
偶発的な「継続の発見」により、超強力な「継続」が仕様に取り込まれたわけだが、一方、「使い方が良く分からない」と言う意味ではcatchthrowの方がマシだったろう。
これは言っちゃえば、「抽象化により分かりづらくなった」失敗例だ、って考えて良いと思う。
そしてC言語のポインタも「メモリ上のアドレス」と「そこに対する参照」の抽象化に失敗した例だ。

※5: 元々は、だ。
しかし、Borland社のTurbo Pascal(後のDelphi)はC言語実装同様、ネイティヴコード(つまりアセンブリ/マシン語)にコンパイルするようにした。やはりミニコンと違って、当時のPCでは「仮想マシン上で実行形式を動かす」のは大変だ、って事が分かったから、だ。
そのお陰で、「C言語に比べるとコンパイル速度が速い」Turbo PascalはC言語を向こうに回して善戦し、DOS時代を通じて二大実装のウチの1つを担うわけだ(そしてライバルは同社のTurbo Cだった・笑)。
なお、SSI社のAD&D、GoldboxエンジンはこのTurbo Pascalを用いて製作されている。

※6: これも何度も言ったが、実のトコ、C言語には数値以外は「何もない」。配列は剥き出しのメモリそのものだし、そこで抽象化をしてない、って事は「配列に終端が存在しない」。つまり、C言語の配列には「長さ」と言う概念がない。
そして中に入れられるのは数値しかない、って事は「文字」も「文字列」も事実上存在しない。機械語を扱った事がある人ならピンと来るだろうが、そう、機械語に実態はかなり近いんだ。
そして「文字」も「文字列」も存在しない、って事はデータ型もない。だからデータ型も(C23では改良される、と言う話だが)判別出来ない。C言語で言う「型」は単に、そこにある数値データを「どう解釈するか」のタグでしか無いから、だ。従って、他の言語で言う「静的型付け」のレベルにも達していないんだ。
言わばC言語は直接機械語を抽象化してる、とは言えるが、それ以上のものではない。

※7: C言語系の「関数名」はAT&T方式で「簡略し過ぎ」で良く分からん、ってのも困ったトコだ。
mallocとはmemory allocationの事で、そのまんま「メモリ確保」だ。
よって、「マロック」って読みたいトコだが、「エム・アロック」の方が原義に近いだろう。

※8: ここにオリジナルではないが、1978年の大ヒットマイコンゲーム、スタートレックのソースコードがある。BASICで書かれてるがそこが問題なのではない。
とにかく、条件で引っかかると「何行目へと飛べ」と書かれてて、全体の把握が困難なソースコードになっている。しかし「昔はそうだった」んだ。
結局昔は、こういう「ジャンプまみれのソースコードを書かなければならない」からフローチャートが役立ってたんだ。

※9: ちなみに「代入」も数学用語だがこれはちょっと補足が必要だ。
英語では数学の「代入」はsubstitutionだが、プログラミングで使われてるのはassignmentだ。assignmentは「(仕事を)割り当てる」と言う意味で、数学用語ではない。
僕ら日本人の感覚だと「変数xに3を代入する」と言われたらxに3を突っ込む事をイメージするだろう。従ってプログラミング上、「変数xに3を代入する」と言うのは納得出来る。
一方、英語のsubstitutionは本来「置き換え」、つまり「置換」を意味する。すなわち、「xに3を代入する」のではなく「xと3を取っ替える」ってのが英語でのニュアンスになり、これは日本語の「代入」のイメージとは大幅に違う。
そしてこのままだとメモリxと3を「取っ替える」事になってしまってそぐわない。従って、「数学用語ではない」assignmentを使ってるわけ。
言いようではassignmentを「代入」と訳したのは誤訳だが、一方、「数学的センス」のある日本人が敢えて「代入」、と数学的メタファで訳したのは上手い手だった、と言わせてもらおう。

※10: これが件のサイトで「実行文の前で変数を宣言しなきゃならない」と記述された根拠だ。
ただし、これはアメリカ国内で1980年代末期で制定された「標準C」、つまりANSI Cでの約束事であり、今は違うわけだ。
従って、K&Rは「Cのバイブル」等と呼ばれてるが買ってはいけない(笑)。既に仕様が古く、現行のCとは大幅に違っている。これを読み終えても「C言語を知った事にはならない」。もうそれは「C言語じゃない」んだ。

※11: ただし、ある程度プログラミングに慣れたら把握しても良い概念だ。Lisp等の関数型言語では「代入」ではなく「束縛」と言うが、これがザックリ言うとこのシステムだ。変数はポインタを使って実体を持つメモリと「束縛」されている。
Pythonも事実上、変数は「代入」の為に存在してなく、ポインタによる「束縛」の為にある。
  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

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

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