読者です 読者をやめる 読者になる 読者になる

非公式 RT

 Twitter のクライアントソフトが続々開発中止を発表しており、最近何だか Twitter を取り巻く色んなものが残念に感じられるようになってきました。えぇさて本稿は非公式 RT についてです。ハイ、3 年は遅れた話題ですね。
 「"公式 RT" vs "非公式 RT" の議論」は Twitter 上で何度も展開されたし、「その話題もう飽きたわー」って言うことにすら飽きた感があります。とにかく非公式 RT を嫌う人がある程度存在するという感触は皆さん持っているのではないでしょうか*1。で、非公式 RT の賛否による対立は「所詮、好きか嫌いかの領域のハナシに過ぎない」ということを皆が共有する事で、割とさっぱり解決しているように感じます。相手とコミュニケーションを取りたいんだから、相手の好みに配慮するのは当然ですよね。
 しかし非公式 RT を嫌う人の中には「非公式 RT されたらその相手を無条件でブロックする」ポリシーを貫いている人がいます。僕は、それはちょっと傲慢だろうという気がします。非公式 RT の賛否対立における落としどころは、あくまで「相手の嫌がることはやめようぜ、それがコミュニケーションってもんだろ」であって、「俺が制定した Twitter 利用ルール(= 非公式 RT 禁止)に反する奴には、コミュニケーション断絶の罰を与える」ではないと思うんですよね。また、そもそも非公式 RT による弊害は以下のようなものであったはずです。

  • 多段になると名前が切れることがある
  • 多段になると文章が切れることがある
  • 自分のどの発言に対する返信なのか明確で無い場合がある
  • 悪意を持って改変する人が一人いるだけで、誤解が広まる恐れがある

実際にはどの被害も出ていないにも関わらず「うわー、非公式 RT された。はいブロック〜」というのは旁若無人な気がします。
 「俺の発言をいつも読んでる人は、俺が非公式 RT した人をブロックすることも当然分かっているはず」っていうのも何度か目にしました。でもそんなのいちいち覚えてないってのが割と普通の感覚だと思います。もし非公式 RT されるのが嫌なら、その旨を伝えるほうがベターでしょう。もしくは無視。もしくは皮肉で返す。そういう対応をした上でまだ続くようならブロックで良いだろうけども。

*1:僕は非公式 RT の活用について賛成的なスタンスなので、非公式 RT を嫌う人の発言を引用するときには非公式 RT を使わないようにしているつもり

unkode-mania でウンコ 300 個ゲットした

 食事はお済みでしょうか?ウンコの話を書きます。

 先日 GIGAZINE でも話題になった『ウンコード・マニア』というサイトがあります。このサイト、要はウンコみたいに酷いプログラムを投稿してシェアしようぜ、という旨のサイトです。投稿されたコードは誰でも見る事ができ、「これはウンコだなぁ」と思ったらウンコマークをつけられます。つまりウンコマークが多ければ多いほど臭いコードだというわけです。

 ウンコードといえば少し前に @lalha2 さんがこういうことを呟いていましたね。

この呟きではウンコードというネガティブな表現を「ひよコード」というプリティな表現に転化させていて素晴らしいと思いました。とはいえ実際は、ひよこはひよこ、ウンコはウンコだなぁというのが僕の開発経験による率直な所感です。

 さて、このサイトは結構面白い企画だなぁと思っていて、僕もちょぼちょぼと投稿していたんですが、先日めでたく獲得ウンコ数 300 を超えたので感じた事とかを書いておこうと思います。あ、ちなみに僕が投稿したのは以下 11 件。

タイトル 言語 ウンコ数 コメント数
お前は何も分かっちゃいない C++ 49 6
名前が似てると気付かないよね Ruby 18 3
無限ループ楽しい C 20 11
n × 4 バイト C 37 7
short から int への変換 C 30 10
NULL と 8 の何故 C 52 10
インド人を右に Java 15 6
switch-caseは現場の臭いがする C 44 16
WinMainの1行目からウンコ C 5 3
ダイナミック型変換 C++ 15 0
スベテカンスウ Java 45 13

1. ウンコにも質がある
 現在サイトには多数のウンコードが投稿されていますが、その中にはウンコマークが山ほどついているものもあれば全然ついていないものもあります。良質のウンコードは誰が見ても明らかにダメダメなコードですが、そうでないものは何処がどの様にウンコなのかが分かりにくかったり、「それはウンコではないよ」と指摘が入っていたりと、色々理由が見つかります。一部では投稿者のレベルが高すぎて見ている人がついていけないケースも見受けられますが、おおむねウンコードの質というのはウンコがウンコたる様子をいかに伝えられているかという点に強く依存して決まるように感じます。

2. ウンコを見つめていると美しいコードを書く事の難しさが分かる
 ウンコードを眺めていると、「これがウンコである指摘は出来るけど、じゃあコレを完璧に正しく美しいコードに修正するとしたら、どう書くのが良いだろう?」と悩んだりします。まぁもちろん "それは状況によって変動するから完璧なんて無い!" 的な発想はあるとしても、ダメなコードを知る事は美しいコードを書くことの難しさを知る事に直結するように思います。

3. ウンコの排出されやすさが、プログラミング言語の質を物語っている
 @ichiro_satoh さんが↓こういうことを呟いていました。

僕はウンコードを見てバカにして笑うことと工夫を考えることは別の事だと思っているので、バカにして笑ったところで技術屋としての資質を疑ったりはしませんが、ウンコードにならないようにする取り組みが実際の開発現場において有益であることは間違いないでしょう。「完璧なコード」とまでは言わなくとも、「ウンコードではないことそれ自体」は現実的に獲得可能な価値であるということです。ウンコードの投稿数を言語別で見てみると、ウンコードを量産してしまいがちな言語とそうでない言語があるのが分かります。

言語 投稿数
Java 44
C 38
JavaScript 19
PHP 18
C++ 17
C# 12
VB.net 10
Python 3
Ruby 1

 もちろんユーザーの多い言語ほどウンコードの投稿数が多くなる傾向が強いのですが、Pyhon と Ruby の投稿数の少なさには注目すべきだと思います。Ruby なんかは日本でユーザーも多いし、もっと投稿があっても良さそうな気がします。しかし僕も Ruby のウンコードを投稿しようとしてようやく気付いたのですが、Ruby って比較的ウンコードを作りにくい言語なんですね。Twitter や勉強会でよく「Ruby は書くのが楽しい」とか言われてるのを目にしますが、それってつまり Rubyist が能天気なのではなく、Ruby がダメなコードになりにくい言語仕様に仕上がっているという事なんだろうと思います。

4. 「ウンコード・マニア」というサイト自体のウンコっぷり
 僕はこのサイトに対しておおむねポジティブに捉えているのですが、サイトの主たる機能がウンコを晒すことである以上このサイト自体のウンコっぷりを(要望受付を通さずに)晒すことはアリだと思っているので、いくつかウンコっぷりを書こうと思います。

i.コードの微修正でウンコマークの位置がずれる。
 このサイトでは投稿に対してウンコマークがついた後でも、コードを修正することができます。コレ自体は良いのですが、ウンコマークの位置を恐らく行数で管理しているのでしょう、ウンコマークの位置がずれます。コードが変わったのだから多少の食い違いはしょうがないのですが、例えば空行を削除しただけでウンコマークの位置がずれるのは嬉しく無いです。コードの変更を許可するなら、diff を使ってウンコマークの位置を補正すべきでしょう。プログラムソースを扱うサイトなんだし。

ii.コードの最大文字数が 1024。
 どう考えても短いです。ウンコードとは長くなりがちなものだというのに。

iii.ウンコマークが付けば付くほどソースが読みづらくなる。
 人気な投稿ほど可読性が下がるとかアリエナイ。投稿されたウンコードの質が下がります。恐らく、それを考慮して「ウンコマーク非表示ボタン」が用意されているのでしょうが、それだと企画の面白さそのものが損なわれます。例えば 10 個ウンコマークが溜まったら大きいウンコになる等の演出をするべきでしょう。

iiii.(8/23 11:05 追記)「まいた」
 


5. サイト利用者がウンコにならないために
 すごく大事で当たり前のことなのですが、実際に仕事で書かれたコードを投稿するのは避けましょう。業務で書いた/見つけたコードをこういう場所に晒すとか、コード以前に人格がウンコです。サイトの about ページにも創作に限定しろと注意書きがありますし、投稿時にも著作権を侵害しない旨のチェックボックスが置かれています。

「こんなプログラマはアジャイル出来ますって言ったらアカンやろ」http://goo.gl/twvpi について。

アジャイル」「テスト駆動開発」「リファクタリング」をマシンガンのように賞賛していて、最近炎上している話題になっているこの記事ですが。
『こんなプログラマはアジャイル出来ますって言ったらアカンやろ』メソッド屋の日記

  • 「1.〜4.」が二つある。
  • これが大切、これが重要、という主張が山ほど書いてあって結局どれが大切で重要なのか分からない。
  • 想定読者が場所によって同業者、顧客、開発者の卵、とバシバシ切り替わっていてメッセージ性が薄れている。

【結論】記事自体をリファクタリングすべき。

一流のプログラマは例外なく我慢強い

 プログラマはかくあるべき、という意見は様々ありますが、そのうちの多くは「プログラミングという作業をいかに効率的に遂行するか」という指標に沿っている気がします。例えば「知識の幅が広い」とか「設計を考えるのが速い」とか「自動化を徹底する意思が強い」とか「問題発見能力が長けている」とか「高いコミュニケーション能力でチームの意思決定を促進できる」とか、そういった類のものです。一般にプログラマの仕事はプログラムを作ることですので、それを速く安全に進めるプログラマほど優秀である、ということに異論は無いかと思います。世の中には Excel 漬けのプログラマだっているそうですが、基本的にプログラマと名がついている職はプログラムを作るという作業がそこにあるからこそ発生しているとみなすのが妥当でしょう。
 そうした "プログラマはかくあるべき" という外面的な観点から、"俺らプログラマはこういうことがしたい" という内面的な観点へ目を移してみると、ずいぶん重複した要求があることに気付きます。例えば「もっと色んなことを知りたい」とか「○○と××を組み合わせて設計すると面白そうだ」とか「△△は面倒だから自動化したい」とか「□□の辺りに問題がありそうだから議論したい」とか「もっとチームがうまく回ってゆくルールを提案したい」とか、そういう類のものです。こうした、プログラマの内なる欲求の本懐は、企業や顧客によらず万国共通であるように感じます。例えば "顧客の笑顔が見たい" というたった一つのピュアな親切心だけをモチベーションに日夜努力を続けるプログラマは恐らく少数派ですよね。

 まぁとにかく、プログラマと非プログラマがだいたい同じ方向を向いているからこそ、今こんなに多くのプログラマが存在しているんだろうと思います。

 さて、プログラマ達がだいたい同じ方向を向いているのであれば、その能力差は何らかのスカラ値で比較できそうな気がします。いまチームの目前に「○○な機能が欲しい」という欲求があったとします。広い知識を持ったプログラマ A は「それは××と△△を組み合わせればできる」と即座に提案できますが、そうでないプログラマ B は「ちょうどよいものがないか調べてみます」と言うことしかできません。仮にプログラマ B が 3 時間で調査を完了させて、プログラマ A とまったく同じ結論にたどりついたとしたなら、その能力差は少なくとも 3 時間であると言えます(実際はもっとだろうけど)。他にも、愚かな設計のせいで後々の拡張やメンテに手間がかかるとか、同じような手動オペレーションを何度もやるせいでいちいち遅いとか、連絡すらまともにできないせいでコミュニケーションコストが増大するとか... まぁとにかくイロイロな理由によってどんどん差が開いていきます。「プログラマの能力というのは(略)軽く5倍から10倍は違う」とか「コーディングの生産性で10倍、コードレビューの速度では6倍もの能力差がある」などと言われるように、確かにそこには明確な能力差があるのです。この差こそが、優秀なプログラマを優秀たらしめているのでしょう。

 こうやって blog の記事を読んでいるときぐらいは忘れていたいですが、開発現場はときに、重大な設計ミスやクリティカルなバグに襲われます。これはスコールみたいなもので(前兆が有ったり無かったりの差はあれど)、大抵は事態の収束を目的としてリソースが投入されます。しかもこういうトラブルに限って、ウンザリするぐらい根が深かったりするんです。もちろん、事態の収束のために営業の剛腕の御力に頼ったり、涼しい顔でクレバーに仕様変更をキメる場合もあるかと思いますが、それらでは代替できない場面というのも確実にあるのです。対処にあたるプログラマは少なくとも優秀である必要があります。しかも、事態を収束させるためにはぐらつく積み木の城を崩さずに改築するかもしくは海底に潜ってそっと小石を置いてくるかそれとも車を運転しながら針の穴に糸を通すかという感じの非常にナーバスなアタックが必要とされます。しかしそれは、"俺らプログラマはこういうことがしたい" には含まれていないのです。つまり我慢強いプログラマでないと切り抜けられないピンチなのです。僕の少ない経験からいうと、こういうとき、一流のプログラマは本当に我慢強く対処にあたり、やがて事態を収束させます。一流のプログラマ、皆さんの身近にもたくさんいると思います。

 というかむしろ、そういう態度こそが「一流とみなされる理由」だし、「非プログラマによるプログラマ評価の核」でもあるように思います。なぜなら、目前のピンチは三流のプログラマでは手に負えないからです。ただ能力が有るか無いか。true or false の領域です。上で、プログラマの能力差は 3 時間だとか 10 倍だとか書きましたが、そういう"時間を買いさえすればカバーできちゃいそうな差"とは質が違うような気がするんですよね。本稿では、「一流の定義は...」とかそういう言葉遊びは面白くないのでやらないですし、別に「○○なプログラマこそが優秀である!」とか「××こそがプログラマのあるべき姿である!」とか声高に叫ぶつもりは無いです。ただ言いたかったのは「デスマでヒィヒィ言ってるプログラマの人たち、辛いだろうけどちゃんと信頼を勝ち取ってる(評価されてる)と思うよ!」ってこと。


 ... というわけで「偉そうなこと書いときながら、僕はぜんぜん優秀でも我慢強くも無いなー」とか反省してるうちに何だか満足したので、今日もご飯がウマいです!

C 言語にポインタがある理由は省メモリ化・高速化・開発作業の省力化です

 前回の記事『プログラム初心者にC言語のポインタを不本意ながら教える羽目になったなら、こう教えると良いよ』でポインタの教え方を書きました。ソレに対して「そもそもどうしてポインタっていう仕組みがあるの?」という質問をもらったので、つらつらと書こうと思います。本稿は「ポインタがある理由の教え方」ではなく「ポインタがある理由」です。分かっている人には相当に退屈な文章ですのでそういう人は読まずにお帰りください。
 で、えーと、結論だけ先に言うと省メモリ化のため、次に速度アップのため、そして生産性アップのためです。


1. メモリは有限である。
 マシンに搭載されているメモリには限りがあります。メモリ空間は広大ですが、無限ではないのです。

 好き放題にどんどんメモリを使ってデータを格納するわけにはいかないというわけです。しかしプログラムは計算のためにメモリ空間を占有します。仮に↓こんな感じに、わずかな有限メモリにどうしても計算に必要なデータ a が格納されている場合を考えます。

 これを別の場所にコピーしたいと願っても、それは不可能です。なぜなら a 以外の場所に a と同じサイズの領域が余っていないからです。もっとメモリ空間が広ければコピー可能ですが、ジャブジャブ使うと、また上限に達してしまいます。これは避けようのない事実です。つまり、メモリを無駄遣いしたくないという要求があります。


2. 処理時間は有限である。
 一般に、プログラムの処理は遅いよりも速いほうが良いとされます。ゲームプログラムでスピード感が損なわれたり、Web ブラウザで読み込みが遅いとイライラしますよね。プログラムは、計算ステップを踏めば踏むほど遅くなります。それは足し算であったり引き算であったりしますが、データのコピーであったりもするのです。大きなデータを何度も繰り返し、膨大な手間をかけてコピーすると、それだけでプログラムは遅くなります。これも避けようのない事実です。つまり、余計なコピーを避けて処理速度を上げたいという要求があります。


3. 開発工数は有限である。
 プログラマは無駄な作業を嫌います。例えば同じソース断片がプログラムのあちこちに散らばると、修正が必要になったときにその全てを修正しないといけないため、開発に手間がかかってしまいます。同じ処理が書かれている箇所が多ければ多いほど開発時間を奪われるのです。これも避けようのない事実です。つまり、似たようなソース断片はできるだけ一元管理して、無駄な記述を減らしたいという要求があります。


4. ポインタという仕組みは、以上 3 つの問題を解決するためにある
 これは C 言語のハナシではなく、ソフトウェア一般のハナシです。「データそのもの」だけでプログラムを作るのではなく、「データの場所を指すもの」を一緒に活用することでこうした問題にアプローチすることができます。C 言語では「データを指すもの」を表現するための仕組みをポインタと呼び、そのための文法が用意されている、というだけに過ぎません。
 例えばこんな場合を考えてみます。

    • 100 キロバイトのメモリを必要とする BigData 型の変数 a, b, c, d, e がある。
    • int の変数 user_input がある。
    • user_input が 0 だったら a の内容を画面に表示し、1 だったら b の内容を画面に表示し、… 4 だったら e の内容を画面に表示する。

 ソースにすると↓こんな感じ。

BigData a, b, c, d, e;
BigData that;
int user_input = (何らかの入力);
if( user_input == 0 ) {
  that = a;
} else if( user_input == 1 ) {
  that = b;
} else if( user_input == 2 ) {
  that = c;
} else if( user_input == 3 ) {
  that = d;
} else if( user_input == 4 ) {
  that = e;
}
display_one( that ); // BigData の内容を画面に表示する関数を呼ぶ

 ↑このプログラムを、ポインタを使ってより良いプログラムへと書き換えることを考えます。


5. ポインタを使って使用メモリを節約する
 まず、上のプログラムで使われている変数 that はメモリを無駄に消費しています。that は a や b と同じだけのデータが格納できるくらいに大きいからです。プログラムの構造を変えてやればメモリは節約できます。「if 分岐で a や b のコピーを得る」というプログラムを「if 分岐で a や b の位置を得る」ように変えるのです。ここで、ポインタ変数 p を使います↓。

BigData a, b, c, d, e;
BigData * p;
int user_input = (何らかの入力);
if( user_input == 0 ) {
  p = & a;
} else if( user_input == 1 ) {
  p = & b;
} else if( user_input == 2 ) {
  p = & c;
} else if( user_input == 3 ) {
  p = & d;
} else if( user_input == 4 ) {
  p = & e;
}
display_one( * p );

 that はメモリを 100 キロバイト消費していましたが、p は 4 バイト消費するだけで済みます。 p はメモリ上の位置を表現する整数値でしかないためです。


6. ポインタを使ってコピー回数を減らす
 5. で変数 that へのコピーを止めました。これでコピー回数も減ってますね。その代わり p へメモリ上の位置がコピーされるようになりましたが、p が小さいので that へコピーするより低コストで済みます。


7. ポインタを使って開発工数を減らす
 a, b, c, d, e のいずれかの値を表示するプログラムには柔軟性がありません。後から f, g, h,… が増えたときにそのぶんだけ else if を書き加えないといけないのは面倒ですよね。そこで、一つのアイデアを引っ張ってきます。「データを並べて、先頭にだけ名前をつける」というものです。前回の記事で使ったのと同じですね。

BigData arr[5];
BigData * p;
int user_input = (何らかの入力);
p = & arr[user_input];
display_one( * p );

 ここまでの説明で、コピーが減ってソースコードの量も減りました*1。それを踏まえた上で、5. で書いたソースと比べてみてください。ポインタ変数 p は、a や b といった具体的な変数を指すこともできますし、arr[2] のように配列中の任意の要素を指すこともできることに気づくと思います。p が指す先に BigData がある、というただそれだけの仕組みなのでどちらの使い方もできるのです。
 さて、ここで注目すべきは「5 つの BigData がメモリ上に並んでいなくてはいけない」という制約が追加されてしまったことです。これはバカバカしい話です。たかがデータを表示するためのプログラムごときにデータの配置方法を決められてしまうなんて。それを改善するために、さらにまたポインタを使います。
 アイデアは、「5 つの BigData をメモリ上に並んでいなくてはいけない」を「5 つの BigData へのポインタがメモリ上に並んでいれば良い」に変えるというものです。BigData の配列 arr を使うのをやめて、BigData へのポインタの配列 parr を使うことにします。メモリ空間のイメージは↓こうです。

↑こうすることで a や b が実際どこに配置されていても構わなくなります。これを C 言語のソースコードに書き下ろすと↓こんな感じになります。せっかくなので関数にしてしまいます。

void display( BigData * parr[] ) {
  BigData * p;
  int user_input = (何らかの入力);
  p = parr[user_input];
  display_one( * p );
}

 一番最初と比べると、いくらか洗練された事が分かると思います。


8. こんな話は、数あるポインタの使い方のほんの一部でしかない
 ポインタの使い方なんて無数にあります。教科書や本稿のような記事に書いてあるのはそのうちのほんの一部を具現化したサンプルでしかないのです。C 言語のプログラマ達は、ポインタを使ってメモリ使用量を抑えて実行速度を維持し、さらにデータ構造を抽象化して開発速度を上げる、ということを日常的にやっています。どういう使い方がどういう問題に対して有効であるか分からないうちは、他人のソースを読んで学ぶと良いでしょう。
 で、その前に本稿を読んで誤解してはいけないコトを書いておきますね。本稿ではコピー回数が少なければ少ないほど良い、的なニュアンスで説明をしました。しかしその考え方が万事通用するわけではありません。例えば、データのコピーをたくさんとっておいてそれらを戦略的に配置して圧倒的な速度アップを狙う、という場面はよくあります。また、ポインタを使って生産性を上げるという話を書きましたが、それも別の観点からみれば生産性を下げると言われていることにも留意すべきです。これは C 言語のポインタがあまりに強力すぎてバグ混入を容易にしてしまっているコトなどが主な理由です。


9. 「今どきポインタなんか使わざるを得ない C 言語を勉強するなんて終わってる」
 ... という指摘は、僕じゃなくて現場を仕切ってる人に言ってくださいね(何かそういう dis をいくつか受けたので一応)。
 あと、プログラミング初学者の人は「Java にポインタ無いじゃないか、Ruby にポインタ無いじゃないか、なんで C 言語だけポインタあるんだよ!」っていう疑問を持っているようです。JavaRuby は C 言語のメモリ空間云々の話をそっくり覆い隠してくれているからポインタが無いのです。その代償として、C 言語ほど強力なチューニングはできません。しかしそれで良いのです。用途に応じて適切なプログラミング言語を使えば良いだけです。コンピュータを使う以上、ぎりぎりまでハードウェアの能力を引き出したいという要求があるので、その領域を C 言語が担っていて、だからこそポインタという仕組みが搭載されているのです。


10. 前回の記事でいただいたコメントへの返事
 前回の記事で "初心者" さんからこんなコメントをいただきました。

私はさぁこれからCを勉強しよう!としてすぐに挫折した屈強の初心者なので、こんなものを聞いてもさっぱり分かりませんでした。orz
もっと分かり易く説明お願いします。
どこが分からないかって?それが分からないくらい初心者です。。。

と言っても手の付けようがないでしょうから。
(1)ポインタがzの場所を教えるとありましたが、なぜ直前に出て来た変数aやbには必要ないのでしょうか?
(2)ポインタがメモリの有効利用が目的とありますが、ポインタを書かなければその分そちらの方がメモリが少なくて済むのではないでしょうか?
(3)ポインタを指定しない場合に起こる不都合というのを具体的に教えて頂けないでしょうか?(VBA程度は分かりますので、そこでの変数にはポインタがなくても不都合が生じません。なぜCには必要なのでしょうか?)

 回答、以下の通りです。
(1) もし "ポインタに数値を代入するとその場所に変数が生成される" と思っているのであればそれは間違いです。まずメモリが先にあって、それを指すデータをポインタと呼びます。質問された例でいうと、ポインタが「z の位置」を決定づけるのではありません。z があるから「z の位置」を得ることができるのです。z から「z の位置」を得る方法は "& z" です。もちろん z があるからといって「z の位置」を得ないといけないとか、z を指すポインタ変数を作らないといけないという決まりもありません。変数を指すポインタが無くても変数は存在できますし、ポインタが 300 個あっても構わないのです。直前に出てきた a や b は、「a の位置」や「b の位置」を使う必要が無かったので登場しませんでした。
(2) えぇと (1) の回答と本稿があれば、恐らく (2) の回答は必要ないですね…。ポインタ以外の方法を使ってプログラムを書くよりも省メモリで済ませられる場面が多いから、ポインタはメモリの有効利用に寄与するのです。
(3) これも (1) の回答があれば回答の必要は無さそうですね。ポインタという仕組みが無いと、省メモリ化、速度アップ、開発速度の向上ということが絶望的にやりにくくなります。だから必要です。初心者さんが VBA でポインタを使わなくても事足りているのは、本稿の 9. で書いた JavaRuby を例に挙げたように、ポインタが不要だから覆い隠されているためです。

 以上。分かんなかったら Twitter で聞いてください。

*1:データが増えたときは arr[5] の 5 を 6 なり 7 なりに書き換えれば済むので else if を書き加えるより手間が減る

プログラム初心者にC言語のポインタを不本意ながら教える羽目になったなら、こう教えると良いよ

 僕がプログラミングに触れた当時は、プログラミングといえば「まず C 言語」でした。それから 10 年以上が経ちました。学校の授業や企業の研修では未だに C 言語を教えているところがあるようです。関数型プログラミング言語という波が来ている 2012 年にもなって未だに C 言語をやっているというのはまるで進歩が無く残念な気もしますが、比較的多くのプログラマに浸透している共通言語を最初に教えるというのは、一方では喜ばしい事だと解釈する事もできるのかもしれません*1。まぁとにかく、本意にせよ不本意にせよ現場で プログラム初心者に C 言語を教える羽目になった 人がたくさんいて、プログラム初心者なのに C 言語を学ばざるを得なくなった 若者がたくさんいるということです。

 C 言語を教えるときに避けて通れないのがポインタで、プログラム初心者が C 言語を学ぶときにやたらとつまずく人が多いのがポインタです。ポインタの理解ごときに若者の貴重な時間を大量に浪費するのはアホらしいので、こういう教えかたすると早く教えられるよ、というハナシを書こうと思います。本稿は「ポインタの解説」ではなく「C 言語のポインタの教え方」です。

1. そもそも "変数は箱"、という例え話がポインタの説明をややこしくしている
 変数という概念を、全く別のメタファーである "箱" に喩えて説明するのはよく使われる手法ですが、その喩えはポインタの説明をするときに破綻します。「箱にラベルを貼るのがポインタです」という "喩え話続行戦略" にはそもそも無理があるのです。どうしても喩えを持ち出したいのであれば、その喩えを一旦信じてもらって理解をレベルアップさせた後、その喩えがなぜ妥当であったかを先生と生徒が共有できる程度まで噛み砕いた説明を付け足して、理解を定着させる必要があります。これはハイレベルな説明テクニックです。喩え話をした後の責任が取れないのであれば、その喩えはゴミ同然でしょう。

2. 「ポインタを教える」ために変数を教える
 ご存知の通り、C 言語のポインタは変数の説明抜きには説明できません。また、メモリの "効率的な利用" という基本的な存在意義があります。従ってメモリ空間をイメージさせることが、変数とポインタの両方を理解させるための近道です。もちろんメモリ空間のイメージを抜きにして文法だけで理解させることも不可能ではありませんが、その場合は C 言語のポインタ変数の宣言周りの文法で間違いなくハマります。表面の文法だけを舐めて理解させようとするのは、かなりハイレベルな説明スキルが必要だということです。相手はプログラミング初心者です。屈強な相手です。この戦略は避けるべきでしょう。

3. 「変数を教える」ためにメモリ空間をイメージさせる
 メモリ空間をイメージさせるために、もはや文法の説明は不要です。C 言語の説明を文法から始めるのは、きっぱり諦めましょう。C 言語のポインタの理解には概念の理解が絶対です。概念の説明をするために文法から説明するのは、少なくとも "C 言語のポインタを説明する" というミッションにおいて遠回りなのです。その理由は上で説明した通りです。メモリ空間をイメージさせるために大切なことは「文法の説明をしないこと」です。文法の説明無しに、方眼紙のようなメモリ空間をイメージさせるのはさほど難しくないはずです。しかし方眼紙状のイメージだけではイメージとして完結しないので、さらにもう一歩引き下がってステップを踏むのが効果的です。

4. 「メモリ空間をイメージさせる」ためにバイトという単位を教える
 バイト、バイト、バイト、バイト!!!ここから全ての説明を始めるのが良いです。「バイトと言っても、アルバイトとは違いますよ」とか小寒いおやじギャグを添えたりして、まずバイトを説明するのです!
 バイトとは、0 〜 255 のいずれか 1 つの整数値を表現できる単位です*2

5. バイトの解釈によって色んな表現が可能であることを説明する
 例えば、あるバイト x が 3 である場合、それをそのまま 3 と解釈することもできるし、「x は 100 からのオフセット値である」とするなら 103 と解釈することもできます。「x は 'A' から並ぶアルファベットの文字である」とするなら 'D' と解釈することもできます。
 「x は○○スーパーに並ぶ缶コーヒーの在庫数である」とするなら在庫数 3 と解釈することができます。しかし缶コーヒーの在庫数が 0 個であるか 42 個であるか 300 個であるかを表現するバイトは存在しません。なぜなら 0 〜 255 では表現しきれないからです。そこで、0 〜 300 個の缶コーヒーの在庫数を表現できるようにするために、1 つのアイデアを与えます。

新しいバイト y を使って、x との組み合わせで数値を表現するというアイデアです。y が 0 のとき、x の値はそのまま缶コーヒーの在庫数と解釈することにします。y が 1 のとき、x の値は 256 ~ 511 個の缶コーヒーだと解釈することにします。バイトが 2 つあれば 65536 種類の値を表現することができます。バカみたいに簡単なハナシですが、バイト数が多ければそれだけ多彩な表現ができることを理解させましょう。
 あるバイト x をどう解釈するかは、プログラマが指定します。C でプログラムを書くということは、そのバイトが何を意味しているかをコンピュータに教える作業をするという事でもあるのです。

6. メモリ空間をイメージさせる
 コンピュータは計算を大量にしないといけません。従って、y が 1 のときは x の解釈は… という具合に 1 バイトずつ考えてゆくのは、とんでもなく大変です。そこで、もう一つアイデアが必要になります。それは例えば「x から始まる 2 バイトを数値として解釈する」という具合に、名前を消してしまうイデアです。これを貫徹するためには、その 2 バイトが並んでいる必要があります。場合によっては 4 バイト、8 バイト... 何にせよ並ぶ必要があります。そこで、バイトをずらっと並べたものが用意されているわけです。これがメモリ空間です。*3

7. 変数を教える
 メモリ空間にバイトが並んでいる理由が説明できたなら、変数をイメージさせるのは簡単でしょう。

 箱のイメージではなく、数バイトのメモリを指す名前として変数をイメージさせることができました。次にこのイメージを使ってプログラムを作ることを説明すると良いと思います。ここで、少しだけソースコードを書きます。4 バイトで表現された数値を扱うために int という名前が用意されていることも含めて、教えることができるはずです。

int a, b;
a = 258;
b = 5;
printf( "%d, %d", a, b );  // 258,5
if( a < 200 ) {
  a = a + b;
} else {
  b = a - b;
}
printf( "%d, %d", a, b );  // 258, 253

 ここまでくれば C でプログラムを書くということがどういうことかを、何となくイメージさせることができます。

8. ポインタを教える
 僕らは普段の会話で「あれが欲しい」「それを見たい」という表現をします。マウスカーソルを動かしてアイコンをクリックします。具体的な対象の代わりに、指し示す表現を使っているわけです。「指す」ことを、プログラムの中でも実現するのがポインタです。

「z の場所がどこであるか」は、「メモリ空間の何番目であるか」で表現できますよね。ポインタなんぞ、この程度に簡単なものでしかないです*4。次にポインタの文法を説明します。

9. ポインタの文法を教える
 ポインタの文法を説明するときに大切なことは、変数の宣言時初期化構文を見せないことです。

int a, b;
int * p;
a = 100;
b = 200;
p = & a;
printf( "%d", * p ); // 100
* p = b;
printf( "%d", * p ); // 200

 & 演算子が a の位置を表現する事、* 演算子で p が指すメモリを表現する事を説明します。

10. 配列をポインタと絡めて教える
 C 言語をいかにして教えるか、という議論において、配列とポインタを絡めるとややこしくなるからダメという意見が根強いですが、それは "変数は箱" という喩えを使っているからです。C 言語を扱うときに配列とポインタは切っても切れないのが事実なので、関係が強いことをアピールすべきでしょう。メモリ空間のイメージがあれば関係の強さもまた容易に理解させることができます。

 文法の説明なぞ、二の次で良いのです。

int arr[3];
int * p;
arr[0] = 260;
arr[1] = 259;
arr[2] = 8;
p = arr;
printf( "%d%", p[1] ); // 259

11. NULL を教える
 ここまでの説明パスを経ることで、4 バイトのメモリを、メモリ上の位置を表現する数値だと解釈すること*5がポインタであることを理解させることができているはずです。次に、ポインタが「今はどこも指してないよ」を表現するために NULL という定数値があることを教えます。こういう説明順序を踏まえることで↓このコードが意味的におかしいことをすんなり理解させることができます。

int a;
a = NULL;

12. その先
 以上が、プログラム初心者に C 言語を教えざるを得ないときにポインタを教えるための説明パスです。この先は省略しますけども、メモリ空間のイメージを用いて説明しているので、以下のつまずき箇所を説明するときのハードルもぐっと下がります。

・2 重ポインタ、3 重ポインタ
・矩形配列とジャグ配列
・関数の引数に n 次元配列を渡すときのコンパイルエラー
・int * から double * への型キャストが不適切な理由
・文字列が配列で表現されること
・関数ポインタ

そんなわけで、ポインタの説明(というか間違った理解を補正するための補足説明の嵐)に時間と手間をかけるのはもう辞めようぜ!というお話でした。

*1:それを意図しているかは別として。

*2:7bit や 9bit の処理系もあるとか、そういう厳格性を問う議論はここでは不要でしょう

*3:バイトが並ぶっていう日本語は妙ですが、まぁそこは汲み取ってください

*4:ポインタとポインタ変数は違う、という指摘が入りそうですがそれはごもっとも

*5:4 バイトとは限らないとかそういう話は今は置いておきましょう

コマンドラインでタイムラインを眺める

Mac でフルスクリーンでターミナル開いて作業してると Twitter が見れません。作業には集中したいんだけどタイムラインが流れていないと禁断症状が出て危険です。なのでアプリを作りました。

これで安心して作業できますね。
『PG-kura / Twitbox』Github