プログラマの理解の基盤

■[雑記/備忘]「常識」というよりは「理解の基盤」と「説明の方法」

檜山さんのところで興味深い話が。
昨日のエントリから続いている話ですが、昨日とは異なり、今日の話は読者への問い掛けになってます。

昨日のエントリでは、檜山さんが書かれている `プログラマのための〜' シリーズのエントリを読む読者となる `プログラマ' に対して、檜山さんが仮定する前提知識 (いや檜山さんからの要求事項 (Requirement?) でしょうかね) を定義したのだと思います。
そして、そのエントリに対しては色々とコメントが付いている訳ですが、`常識' って言っちゃうのはちょっと…… という感じコメントもありますね。

まあ、ここで仮定した内容にも、その前提部分での更なる仮定がありますよね。スタックメカニズムありきという具合に。
スタックフレームを仮定してしまうと、スタックメカニズムの存在しない処理系 (メインフレームなど) のプログラマにはちょっと苦しいかもしれないなあ、などとは思ってしまいました。
いや、メインフレーム (汎用機) でも C 言語などの実行環境が載ってるならエミュレートされているので問題ないでしょうが。
;; 私は某社のメインフレームしか知らないので、メインフレームでもスタックメカニズムを備えているものがあるのであれば、何か頓珍漢なことを言ってることになりますが、まあ、少なくとも一つはその様な処理系がメインフレームに存在すると言うことで。

その上で今日のエントリ。
ここでは昨日のエントリにあった様な、プログラマにとっての常識とか前提知識とか、所謂、`暗黙知' に相当する様な概念を、まだそれが培われていない人にどの様に伝えるか、という様なことがテーマになっている様に思われます。
;; いや、勝手な思い込みですが。
しかし、タイトルの、

「常識」というよりは「理解の基盤」と「説明の方法」

からすると、そういうことなのだろうと、書き終えてから気付きました……

これらをその様な観点で明確に伝えることができる様なレベルの人間ではありませんが、ちょっと考えてみます。
しかし、

僕は、実装を一切引き合いに出さない能書き(表示的意味論とか形式仕様とか)は割と得意なんですが、上の状況でそれしたら、アンタ、ソリャチガウでしょ。チガッテナイのは、どんな説明かなぁ。そして、その説明(と理解)の基盤はなんだろうなぁ。

という檜山さんの目的を達せられる様な話にはならなさそうです、ごめんなさい。

str1, str2が文字列だとして、if (str1 == str2)と書いてしまうのはよく見かけますよね(ある意味、自然だと言える)。Cならstrcmp、Javaならequalsを使え、って教えるでしょうが、その理由とか事情とかを心底納得できるように説明するにはどうしますか。文字列がインターンされていれば、==でもよかったりするし。

昨日のエントリにあった、

  • コードもデータも(それが所詮ビットコンビネーションだという意味で)一様なこと。
  • メモリセルには、そのコンテンツ(中身)とアドレスがあること。
  • コンテンツもアドレスも、やっぱりビットコンビネーションとしては一様であること。

あたりがカギかと。

簡単に C なら、Java なら、と書かれていますが、それぞれ背景は異なります。
共通しているのは、`==' という演算子の両辺に置かれる要素として求められているものとは異なるものが指定されているという点でしょうか。
C にしろ Java にしろ、文字列というオブジェクトの表現方法にはある種の複雑さがあり、演算子 `==' というプリミティブな要素を求める構文には直接指定できない構造を持っていると思います。

つまり、

  文字列という複雑さのある要素を解析して比較するための段取りが必要になる。

ということでしょうか。

また、`等しいか否かを判別する' という `==' 演算子に求める役割としても、両辺に指定された要素の何が等しいことを判別したいのか、という問題も含んでいると考えられます。
等しいメモリセルか否かを判別したいのか、コンテンツのビットコンビネーションが等しいことを判別したいのか、その何れも同じこと (つまり全く同じオブジェクトであること) を判別したいのか、ということですね。

そして、これらを理解して貰うためには、昨日のエントリに書かれている様な「なんかすごく即物的なこと」の幾つかを理解して貰う必要があるのだと、個人的には思います。

例えばここで、「入れもの」と「その中身」の様な説明の仕方はありかもしれません。しかしそれは誤解を生む元となってしまう危険性がある様に感じます。特に初学者に対しては。
まあ、言語によっては、そのメタファが有効になる場合があることを否定することはできなさそうですが。

ローカル変数としてchar buffer[BUFF_SIZE];って宣言して、そのままreturn buffer;として何故いけない?これ、割と問題なしにいったりしますよ(いや、問題ありだけど)。

こちらはスコープ (名前空間も含む) や記憶期間 (寿命) の問題ですよね。
そして、それが言語仕様としてはそうなんだけど、実装によってはそれらが消えて無くなるまでのタイムラグがある (語弊があるか) ために上手く行ってしまうことがあったりして、ちょっと面倒なことになることが多い話ですということで。

これについては、

  • サブルーチンの呼び出し、戻り、スタックフレーム(活動レコード)というメカニズムがあること。
  • 静的に割り当てられるコード/データ以外に、実行時のメモリ割り当てがあること。
  • 実行時の割り当て用の領域をヒープと呼ぶこと。
  • 参照である変数/引数/戻り値は、普通ヒープ上のメモリブロックを指すこと。

辺りが必要だと思うのですが、スコープや記憶期間の全般をこういう形で理解して貰うのは骨が折れるかもしれませんね。

特に Java などの場合だと、インスタンス生成時に何が行われてってところからになってしまい、皆、聞く耳さえ持ってくれないかもしれません。

何故いけないかと問われれば、「無くなってしまうから」としか言いようが無いとも思います。

代入文の右と左で使える式の形を構文ルールとして理解すべきでしょうか。けっこうバラエティありますよ。n = 3*x; a[1] = n + 2; *(p + 1) = a[i - n];とか。

左辺値と右辺値って、恥ずかしながら私は明確には理解できていません。
演算結果と式という位の認識です……

メソッド内で使える「this って何?」と聞かれたら…。

自分自身としか……
そう言う意味で、私は `this' より `self' の方が理解し易いと考えてます。そもそもメソッドの実行という説明よりメッセージを送るという説明を好んでするタイプなので、そういう説明の方が、`this', `self' という概念も受け入れられ易いのではないかと思っています。

時間が無くてどうもまとまらないというか、頓珍漢なことを書いている気がします。
ああ、以前にも檜山さんにはゾウガメ時間の教えを頂いているというのに……