プログラミング入門にふさわしい言語とは?【その2:職業プログラマーを目指すには】
d:id:naruoga:20091009:1255062099 に引き続き、やっと最終回*1。
簡単にさらっと意見をまとめようと思っていただけなのに、なんで三回シリーズになりますか俺。文才ないな。
えー前回と話変わって、職業としてプログラマーを目指す人向けのプログラミング言語談義です。
こっちの方は前回よりもはるかに多彩な意見が存在することが予想され、私ごときではなんらかの結論を出すことはできません。ので、思いついたトピックをだらだらと語っていく形になります。
異論反論はコメントなりトラバなりはてブなりでしていただければと。
前提
大事な前提を前回書いてなかったんですが、これまでのエントリはプログラミングを学ぶ側、教える側、どっちに向いていたでしょうか。
基本的には学ぶ側も向きつつ教える側へ、という感じだったのですが、学ぶ側へ投げかけるには前提とする知識がちょいとアレでソレな気がしました。特に前回。
ので両方に色目を使うのはなしにして、
- プログラミングを教える立場の人
- プログラミングを自習する立場の人
に対象をしぼりたいと思います。片方向で授業を受ける立場の人は対象にしませんよ、ということです。なぜならばそういう人たちには選択肢がないわけですから(授業で教えてくれる言語以外は選びようがない)。
職業プログラマーが学ぶべき言語の要件
前回のエントリでも同様のことを述べましたが、要件の設定はこちらの方が緩くなります。「え、逆じゃないの?」と思う方もいらっしゃるかもしませんが、そーではありません。
なぜなら、職業プログラマーを目指す人間がただ一個の言語を学ぶだけで満足していいはずがないからです。ですから、ある言語では満たされていない要件が別の言語で満たされていれば十分。そのなかで、「最初の言語はなにを優先するか?」ということになります。
例によって私見ですが、考えられる要件は次のようになるのではないかと思います。
必須要件
付加条件
- オブジェクト指向言語であること*5
- 十分に強力なライブラリ群を有すること。あるいは外部ライブラリを簡単に参照できる仕組みがあること。
- 前記ライブラリを除き、言語単体で閉じていること。つまりテンプレート言語の類でないこと*6
- 引数の値渡しと参照渡しが概念として明確になっていること。
よくよく考えたらまだ出てくるかもしれないですが、ひとまずこんなもので。思いついたら追記するかも……。
さてこれらを踏まえて、あとは与太話を……。
「C 言語を通っていないプログラマはダメ」は本当?
この話を書きたくてここまで来たようなもんです。
プログラミング入門言語としての C 言語
まず前提として「入門言語としての C 言語」については、正直当落線上でしょうね。
その一番の理由は、言わずとしれたポインタです。
C のポインタはアセンブラの間接参照を生で見せたようなところがあり(いや、実際そうなんですが)、なおかつそれに文字列操作やら参照渡しの機能を持たせているため、「このポインタはどのコンテキストで使われているの?」ということを常に意識しないとあっという間に混乱します。
このように、(プログラミングとして)本質的でないところで悩ませる言語は、入門に最適とは必ずしも言えないでしょう。
実力のあるメンターさんが適切なテキストを用いて、始めて C でのプログラミング入門は成り立つ、というのが私の持論です。
C を通るべきか否か、その二つの考え
「C をやらない奴はダメだ」という発言を眺めていると、二つの傾向がある気がします。
一つ目は、「俺は C でやってきた、だから今の俺がある、だから C でやるのがいいんだ」のタイプ。いや、結構多いですよ、こういう人。「SEED から見た奴はガンダム語る資格がねえ」とか「アラカン見てない奴はチャンバラ見たとはいえねえ」みたいな感じかな。
こういう人に「C で教育を行わなければいけない理由はなんですか」って聞くと「諸説有るだろうけれど、C が最高の言語の一つであることは間違いなく、だから学ばなければならない」とか答えになってないことしかいってくれないので、こういうタイプはさっくり無視しましょう。
二つ目のもっと腑に落ちる回答は「C のように資源管理を自前でやらないといけない言語を経験しないと、自分のプロセスがどれぐらいメモリを食っているかとか、そういうことを考えて設計できなくなる。だから C をやることは大事だ」というもの。うん、これなら私も分かります。
ただし、「自分のプロセスがどれぐらいメモリを食っているか、そういうことを考えて設計」しなければいけないことが必須であるならば、という但し書きをつけます。
つまり言語やプラットフォームが頑張って、GC とか階層ストレージとか使いまくって、どんだけでかいメモリを alloc しようがパフォーマンスを落とさないような環境。そんなのがあるかどうかは知りませんが、そういう環境でだけ仕事すればいいなら、資源管理なんて意識する必要ないですよね。
資源管理といえばもっと小さな例で言うと資源をオープンしたまま関数がエラーで落ちちゃってその場で return したら資源リークする。これだって ruby とかなら begin - ensuretry - finally Win32 の構造化例外と間違えた (^^;) 使えばいいわけで、これを自分で意識「しなければならない」理由は少ないと私は考えます。
ただし、C が素晴らしく有効なところ、それはコンピュータアーキテクチャを学ぶときの副読本をして見るときで、アーキテクチャの勉強がしたいなら、手段として C は必須になります。高水準言語の記法で、低水準にアクセスできるのですから。「C は高水準アセンブラ」という言葉はこういうときに生きてきます。
また Linux カーネルや Ruby のソースなど、C でかかれていて面白い題材はたくさんあります。こういう物に手を出すなら、当然 C は必須です。そりゃそうです、「道具」ですから。
Ruby
オイラがプログラミング言語教育でなんか一個選べ、と言われたら ruby を挙げる可能性が高いと思います。
メリットは:
10.10 追記
- 動的型言語であり、型について意識しなくてもいいことが多いこと。
一方で、デメリットと言うか、これどうしようかな、というのが、
- オブジェクトの考えが骨格にいるため、オブジェクト指向を早い段階で導入する必要があるが、これが悩ましい。
- ブロックの概念もどう教えるか。IO.open のブロックとか超便利なんだけど、意味をわかりやすく説明できるかというと……。Array.each なんかもそうですね。
- 例外も初めてのプログラミングというにはちょっと重たいか?
- Gems とかを教えるべきかどうか。教えるとしたらどんなやり方がいいか。
- インスタンスメソッドやモンキーパッチングのような Ruby っぽい話を入れるかどうか。後者はともかく前者は話したい所だけど、頭がいい人なら思いついちゃうよね。んで話がそっちに引っ張られないかが心配。
なおあくまでも「プログラミングの入門」なので Rails とかは対象外です。
私なりの感触は、オブジェクトの壁さえなんとかなればいけるかなって感じです。少なくとも Java や C# で教育を行うよりかはよい効果が得られそうに感じています。理由は ruby が big class であることと宣言がないことです。少ないことと、動的型言語であることです。*9
Pascal と Modula-2 と Oberon
Pascal について知らない人はいます? いませんね? といいたいところなんだけど、もう知らない人多いんだろうなぁ。
チューリヒ工科大学 (ETH) の N. Wirth (ニコラス・ヴィルト) という人が作った教育用言語です。コンピュータの実装から離れてアルゴリズム記述をできる言語という目標を掲げた Algol ファミリー、主に Algol 60 の影響を強く受けています。
C と Pascal は対比されることが多いのでここでも C との違いを列挙してみましょう。
- Pascal では関数は入れ子構造を持つ。
- スコープルールも入れ子の内外で適用される。
- Pascal は 1 パスコンパイル*10を指向している
- したがって、前方参照 (自分の前方、ソースコード的には下の方) にある識別子 (主に関数と手続き) を参照できない。参照するためにはあらかじめ foward 文で「こういう識別子があるよ」と指定しておく必要がある。
- 元々の言語仕様では複数ファイルへの分割は考慮されていない。
- C ではすべて関数だが、Pascal は関数 (値を返す) と手続き (値を返さない) は厳格に区別される。関数で左辺値がないとエラーで怒られる。
- Pascal の goto ラベルは英語一文字+数字といういかにも「使うな」という形式しか使えない。
- また break や continue などの goto の糖衣構文も存在しない。おかげで Pascal でコード書くとフラグの嵐になる。ちょっとトホホ。
- ポインタの使い方が厳格。基本的に「他の変数を指す」目的にしか使えない。
- 値呼び (call by value)、参照呼び (call by reference) が文法で定義されている
太字で書いた「ポインタの使い方が厳格」「値呼び、参照呼びが文法で定義されている」の二点が、C の学習の補強として Pascal をやったほうがいい、という私の主張の源です。
C は文法を単純にすることを指向するあまり、ポインタにかなり強く依存しています。例えば:
int my_strcmp(const char *s1, const char *s2) { while (*s1 != '\0' && *s2 != '\0' && *s1++ == *s2++) { ; } return *s2 - *s1 }
(実際にこんな関数書いたり使ったりしちゃダメだよ) みたいなコード普通に見ますよね。Pascal では文字列や配列はポインタとしてアクセスできないので上記のようなコードは書きたくても書けません。index を用意して、s1[idx1] のように書かなければなりません。まどろっこしいですが、間違いは少ないです*11。
では Pascal のポインタというのはどういうときに使うかというと、いわゆるリンク構造を作るときに使います。典型的なのが線形リスト。
type list_cell_ptr = ^ list_cell; list_cell = record value: Integer; next: ^ list_cell end; ... procedure list_add_value(var list: list_cell; value: Integer); var new_cell: cell_list_ptr; begin while list^.next <> NIL do list := list^.next new(new_cell); new_cell^.value = value; new_cell^.next = NIL; list^.next = new_cell end var list_top : cell_list_ptr; begin new(list_top); list_top^.value := 0; list_top^.next := NIL; add_list(list_top, 10) end.
久しぶりに Pascal のコードを書いたので間違いもあるかもしれないし、なにせエラー処理もなにも入ってないのでマネ禁ですが、ポインタ (^) の使い方が非常に限定されていることが分かると思います。
たしか上記ソースで list_top^.next = 123 とかはコンパイルエラーで怒られるはずです。したがって C のようにメモリアクセス手段として使うことはないです *12。
このように「他の構造物を指す」目的にポインタの使用を限定することで
C のような混乱を防ぐことができる理由の一つになります。
もう一つ C のいやらしいのは、基本的には値呼びしか存在せず、参照呼びをポインタで実現していることです。
例えば C で書くこういう関数は (中身は意味無しです)、
void foo(int a, int *result) { *result += a; }
Pascal ではこう書きます。
procedure foo (a: Integer; var result: Integer); begin result := result + a end
「値呼び」とは引数の「値」だけを手続き/関数に渡すことで、「参照呼び」とは引数の「参照」を手続き/関数に渡すことです。参照呼びでは引数として渡した「参照」は手続き/関数の外に波及するのがポイントです。C には参照呼びが言語仕様上存在せず、変数へのポインタを値で渡すことで無理やり参照呼びの機能を実現しています。
こいつは C++ で改善されましたが、Pascal では最初から備わっています。ので、この点でもポインタを使う必要がないのです。
ということで Pascal は今それをつかって何か応用するような言語ではありませんが、C のポインタが分からなくなったら学ぶ価値がある言語だと私は思います。
ただ、まともな処理系が少ないんですよねぇ……。GCC のコンパイラファミリーに Gnu Pascal がいるのでそれを使うか、あとは Turbo Explorer の Turbo Delphi か。ただ Delphi は非常によくできた処理系ではありますが方言がきついので*13「Pascal を学ぶ」というより「Delphi を学ぶ」と割り切った方がいいかもしれません。
なお Wirth はその後、モジュールプログラミングとデータ抽象を持ち込んだ Modula-2、さらにオブジェクト指向化し、Smalltalk のように環境自体をオブジェクトとして自由に操作できる Oberon という環境を作ったりしてますが、まあこういう機能については他の言語で十分学べるのでなにもこんなマイナーな言語で学ばなくてもよいでしょう。
ただし Oberon の動く環境を見たことがある人は非常にまれだと思いますので、デモしてみるとみんなに驚かれたりするかもしれませんね。
Perl/Python
なんでこの子たちが一緒になっているかというとオイラがよく知らないからです (^^;)。
Perl が強力な言語であることは認めます。しかしプログラミング言語を学ぶための教材言語としてはやや文法の自由度が高すぎるのが気になります。つまり「どうとでも書けてしまう」ので、まだ自分のスタイルが固まっていない人が学ぶのはおっかない気がするのです。特に Web 上に散乱する記号の羅列のような不可思議なコードを見ると。
それとも今は「モダン Perl 入門」読め!で済むのかし? それなら選択肢になり得るけど。いずれにせよ、回りに Perl に詳しい人がいる場合前提だと思います。独習は勧めません。
- 作者: 牧大輔
- 出版社/メーカー: 翔泳社
- 発売日: 2009/02/10
- メディア: 大型本
- 購入: 25人 クリック: 534回
- この商品を含むブログ (113件) を見る
Python についてはインタプリタ iPython が強力であること、「Python チュートリアル」が非常によく書けていること*14、などなどから、なかなかいいかな、と思わなくもないですが、日本ではあまり盛んではないこと、他のオブジェクト指向言語では大抵存在するアクセス制御 (private とか public のこと) が存在しないこと、名前空間という概念がちょっと分かりにくいこと、が難点でしょうか。
- 作者: Guido van Rossum,鴨澤眞夫
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2007/09/22
- メディア: 単行本(ソフトカバー)
- 購入: 5人 クリック: 113回
- この商品を含むブログ (56件) を見る
Smalltalk / Objective-C
もともと「自分の環境を自分で好きに触れることこそがパーソナルコンピューティング」という概念を実現するために生まれた Smalltalk。
まだ私自身もかじった程度なのですが、今表示してるウィンドウにメッセージ投げるとぱっと表示が変わるとかいうのはかなり強烈な体験なので、かじってみるといいと思います。
あとオブジェクト指向言語というのは結局クラスライブラリ命なわけで、その点オブジェクトインスペクタとかが強力な Smalltalk の環境に早いうちに触れておくのは悪くないかもしれません。
ただし決して仕事に結びつく言語とはいいがたいので、最初に選ぶ言語としてどうかというとちょっと難しいですね。
Objective-C は Mac OS X の Cocoa アーキテクチャおよび iPhone/iPod Touch の開発環境として有名です。Smalltalk ほどのインタラクティブ性はないですが、言語仕様は非常に似ているので (C の名前はついているけれど C らしいところはほとんどない、そうです)、Objective-C の予習としての Smalltalk は「アリ」かもしれません。
逆に言うと Apple の環境でしかマトモに動かない (GCC には Objective-C のコンパイラも収録されてますが、フレームワークがなければ意味がないですから……) のをどう取るかですね。
Java/C++/C#
この三つを同時に論ずるなんてコイツものを知らないな、と思ったあなた、その通りです。
基本的に静的型言語、スモールクラス*15、という共通項でまとめてしまいました。乱暴なのは認めます。
えっと、Java については、あなたがプログラマとしておまんまを食べたくて、どうしても一個の言語しか勉強する時間が取れないのであれば、怪我がない選択肢だと思います。
と、なんでこんな一般論でお茶を濁すかというと、オイラ Java ぜんぜん知らないからです。Ruby とかをやってる人間からするととにかく宣言が多くてかったるいというのが印象で、もうすぐ四十郎のワタクシが覚えるのはちょっと骨。が、たぶん Ruby でご飯を食べるより Java でご飯を食べる方が簡単でしょう。PHP の方がもっと簡単じゃね? という話はともかく。
C++ は……最低でも STL、できれば Boost と一緒に勉強しましょうね。
自分でテンプレートクラスが書けるのは当然です。
もし組み込みなどの関係で RTTI 禁止、例外処理禁止、STL も禁止、と言われたら、よほどオブジェクト指向分析・設計に自信がない限り、Better C と割り切り、オブジェクト指向を忘れた方が幸せかもしれません。イケてない人の書いた C++ のフレームワークほど不幸なものはないからです。
C# は事情としては Java に近いかな。そのかわり MS と心中する覚悟が必要です。まあ Mono プロジェクトにコミットして NetBSD とかに Mono 移植するってことはできなかないですが。
Javascript
実用性といえばグリモンも Firefox 拡張も書けて Web 2.0 もばっちりなコイツはいい線いってると思います。
ただしオブジェクト指向の考え方に少しクセがあるので (プロトタイプ指向といいます)、そこを乗り越えるのがちょっと大変かもしれません。Prototype.js 使えって話もありますが、私は使ったことないので解説はできません。
あと Web 回りでプログラムを書くとしたら Javascript 自体よりも W3C DOM*16 を理解するというハードルが立ちはだかるので、別に Javascript だから簡単に Web 2.0 できるわけじゃないことは認識しておきましょう (^^;)。
Basic
これは完全な与太話です。
そもそも Basic という言語はダートマス大学のケメニーとカーツという二人の数学者が作った言語です。
当時は数値計算には Fortran、事務処理には Cobol、記号処理には Lisp、アルゴリズム記述には Algol はありましたが実用的な処理系がないという状態でした。
彼らは、数学の道具としてプログラミングを学ぶ学生たちのために、ぱっと書いてすぐ結果が得られる言語が必要であると考えたのです。一種の電卓的言語ですね。
そこで次のような思想が盛り込まれています。
- インタプリタであること。実行の結果がその場で得られること。
- 変数の宣言が存在しないこと。電卓を使うのにいちいち変数を宣言しない。
- ゆえに型宣言も存在しない。後置で記号を書くことで型を示す (たしか独自型は文字列型しか存在しなかったかと)。記号がない「普通」の変数は小数型である。
- 電卓なので大規模プログラミングの概念は最初から捨てている。のですべてグローバル変数なのは当然。スコープルールは初心者にわかりにくい。
- 数学者が好む機能として、行列演算やグラフィカル端末を用いた場合のグラフ描画機能などを有する。
ケメニーとカーツの Basic は ANSI で標準化され、日本でも「JIS 標準 BASIC」として規格化されています。
これらの特徴のうち、最後のものを除いては、初期のパーソナルコンピュータ (マイコンと呼ばれていました) への移植にちょうどいい、小さくてわかりやすい文法でした。
そこでマイコンに Basic のサブセットを実装するのが流行り、その一つがビル・ゲイツとポール・アレンが書いて通信販売で売り、「ソフトウェアを商売にする」というビジネスモデルを確立した Microsoft Basic です。
そんなわけで初期のマイコンには大抵 MS 系の Basic がついてきて使用者が膨大になったこともあり、マイコンの Basic でプログラミングを学んだ人間がよりよい言語……たとえば C や Pascal などに触れることで、「入門用にあえて切った」ところまで批判をする人間が出てきました。
曰く、構造化構文が貧弱である。
曰く、すべてグローバル変数はナンセンス。
曰く、変数を宣言しないのはバグのもとだ。
などなど……。
パソコンの分野では MS Basic と互換性を持ちながらこれらの改善を行う試みがなされました。今の Visual Basic はその完成形の一つと言えるでしょう。
しかし MS Basic との互換性を取るために堅牢な言語であることについては妥協があり、一方で文法が複雑化してしまったため、教育用言語としての Basic の精神は失われたと私は思っています。いや実際便利なときもあるのは認めますけど、VB。
ダートマス大学のメンバーも黙ってこの状況をみていたわけではなく、オールドスタイルの行番号を廃止し (互換性のために使うことは許されています)、構造化構文を強化し、関数のローカルスコープを導入し、モダンな言語として再設計しました。
これが ANSI でいうところの FULL Basic、「JIS 拡張 BASIC」です。
この言語仕様に基づく処理系は True Basic として商品化されたのですが、バグバグだった上に MS Basic と互換性がなかったことが猛烈に批判され、ほとんど使われることなく姿を消しました。
この逸話から明らかなことは、
- 教育用言語としてターゲットを絞って作った言語であっても、その言語に手慣れてしまうと、大きなプログラムを書きたくなる。
- 言語が普及すると、そもそもの設計思想から離れ、批判を受けることがしばしばある*17
- それがために元の思想を離れて大規模なプログラムが書けるようにすると、往々にしてそれは元の思想を生かさない形で発展する
ということです。
教育のための言語として何を選ぶべきか……難しいですね。
長々とつまらないエントリをお読みいただき、ありがとうございました。
*1:しかも書きかけで1週間近く放置してしまった。すみません。しくしく。
*2:ここ、いきなり突っ込む人がいるかもしれませんが、世の中で商売のネタになっているプログラムはほぼ間違いなく手続き型言語で書かれているので、「最初のウチに学ぶ言語」は手続き型であるべきだと思います。関数型や論理型はその次のステージでしょう。
*3:これちょっと分かりにくいですが、Fortran IV や昔の Basic、DOS のバッチファイルなどで、IF 文の後ろに goto しか書けないようなのはダメ、ということです。
*4:「実現」は例えばヒープソートのアルゴリズムを書けること、「利用」は qsort() が使えること、という意味で取ってください。むろん、両方できたことに越したことはありませんが、最低「実現」はできない言語はいろいろ困ります。
*5:これが付加条件かよ! という人も多そうですが、まあ、とりあえずは……。
*6:思いっきり PHP を頭にいれて書いてます。PHP を単体の言語として使うことってあまり聞かないので、単体で学ぶことに意味が薄い、という意味です。もちろん、PHP プログラマになるための職業訓練であれば話は別です。
*7:逆に言えば一つのクラスにメソッドが多いとも言えるけど。
*8:これは有名な話ですが、認知科学の知見によれば、どんなに意味の少ないものであっても(例えば宣言文とか)、行数が多い物はそれだけ理解に時間がかかると言われています。一説によれば人間が一度に認識できる要素数は 7±2 なのだそうです。
*9:宣言が「ない」わけではなくて「少ない」ですね。例えば a = Hash.new は一種の宣言とも言えるわけですから。あと動的型言語については記載漏れなので追記しました。
*10:意味わかんない人は最近新版が出た コンパイラ―原理・技法・ツール (Information & Computing)復刻された「コンパイラ」でも読んでください。雑駁に言えばソースコードを一回舐めるだけで実行コードを生成することです。 (「コンパイラ」って日本では3回出版されてるんですが今のが 2nd edition なんですよね。だから日本で昔出てたドラゴンが表紙の奴の復刻かと思ってたら、今の版は 2007 年に出た改訂版の翻訳であり、復刻というのは間違いだと指摘いただきました。)
*11:個人的考えでは、C/C++ でもポインタを使わずインデックスを使用する書き方にした方がよいと思います。余計にローカル変数は使用しますが、今のコンパイラなら最適化で消えるでしょう。
*12:Turbo Pascal では「メモリ配列」「I/O 配列」といって、配列にアクセスすることで実メモリや I/O にアクセスできるゴーインな機能が採用されてました。まぁ当時の Borland のこういうところは大好きですが。
*13:というより、Pascal を実用言語として使うには言語拡張せざるを得ない。
*14:ただし、純粋な入門書というより、Python はなぜこういう言語仕様になっているか、などの話もちょいちょいあるので、ちょっと「この言語がプログラミングはじめて」という人にはノイズになるかも。
*15:一個のクラスに沢山の機能を盛り込まないで、ちょっとずつ違う複数のクラスを用意するという考え方。逆は Ruby のビッグクラス。Hash や Array というコンテナクラスを考えれば思想の違いがわかりやすいと思います。
*17:Pascal も C の設計者 Dennis Ritchie より「実用言語としては使い物にならない」とてひどい批判を受けています。