FrontPage
メモ †
- 入力、演算、制御、記憶、出力の方法を記述するのがプログラミング言語。
- 文法がどんなに違っても、5大機能をどうするかを書くことに変わりはない。
- (例外的なのは Prolog のようなパターンマッチング言語だがここでは対象としない。)
- 何度も同じことを書かないために、サブルーチン化、関数化、クラス化する。
- よく使うサブルーチンなどを集めたものがライブラリとなる。
- 関数型言語
- 関数(ルーチン)内での入出力がない。関数呼び出しの最初に入力を渡し、最後に出力が返る。
- 記憶や外部からの影響がないため、入力が決まれば出力が必ず決まる。
- 副作用がない。
- 複雑度が小さい。
- 実行順は関係ない。
- 入力と出力をキャッシュすることができる。
- 関数型言語の反対は「非関数型言語」であって「手続き型言語」とはちょっと違う。
- 手続き型言語
- 手続き(手順)を記述する事を重点にしている。
- 一般に、人間が考える時は手続きの方が考えやすい。
- 手続き型言語でもルーチン内での入出力、記憶をなくせば、関数型言語と同じ結果が得られる。
各言語の相違点について †
- 文法が異なる。
- 宣言の有無
- オブジェクトの型の有無
- 変数の型の有無
- 型チェック機構の有無
- 数式表現の差異
- オブジェクト指向表現の有無
- プログラムがデータとして取り扱えるか。(LISP, eval関数など)
- データの取り扱いが異なる。(記憶方法、表現方法)
- ガーベジコレクションの有無
- ガーベジコレクションの方法の差異
- ルーチンへの値の渡し方が異なる。
- 値渡し call-by-value (値そのもの(コピー)を渡す。)
- 参照渡し call-by-reference (値へのポインタを渡す。)
- 値を書き換えると、呼び出し元の値まで書き換わってしまう問題がある。
- 名前渡し call-by-name (値を保持する変数名(あるいは式そのもの)などを渡す。)
- 言語が機能を持つ場合と、ライブラリが機能を持つ場合とがある。
- COBOL は言語に大量に機能を持っている。
- LISP は言語はほとんど機能を持たず、ほとんどがライブラリに機能を持つ。
- 最近は言語は機能を持たず(基本的な定義、条件判断、演算など文法的に特別扱いしたいもののみ持ち)、ライブラリが機能を持つのがほとんど。
- ライブラリが異なる。
- .NET Framework は言語に依存しない共通ライブラリを用意する方針。
- インタプリタかコンパイラか?
- インタプリタ系言語ではコンパイルできないということはない。
- 型のない言語などではコンパイルしてもほとんど効果がないケースがある。
- 低レベルルーチンが十分高速なら、高レベルルーチンでの速度はそれほど問題にはならない。
- 低レベルルーチンは誰が呼んでも30分で注文を届けてくれるピザ屋みたいな感じ。
- インタプリタ
- ソースを逐次解釈して実行する。
- コンパイルが不要。
- 解釈に時間がかかる。
- バイナリ互換性の問題がない。
- コンパイラ
- ソースを低級言語(一般に機械語)に変換する。
- 機械語であれば、CPUがそのまま実行するので非常に高速。
- 機械語ではバイナリ互換性が問題となる。CPUアーキテクチャごとにコンパイルが必要。
- Java のように Virtual Machine (VM) を使う手法もある。
ジェネリックプログラミング(総称プログラミング) †
- いわゆる型パラメータ構文を使うことをこう呼んでいる。
class TContainer<T> {
T x;
void set(T x) { this.x = x; }
T get() { return x; }
}
従来の問題点 †
ジェネリックプログラミングができない、型付きの言語では、
型だけが違う場合、以下のようにして切り抜けていた。
- コピーペーストして型だけを書き直したモジュールを作った。
- プリプロセッサマクロなどで型だけを変更できるモジュールを作った。
- 言語によるエラーチェック機構がない。(エラーがでても何が問題だかわかりにくい。)
- 可読性が低下する。
- 型による特化が困難。
- 汎用性のある型(Object 型など)を使い、どの型でも問題ないようにした。
- 毎回ダウンキャストが必要。
- ダウンキャストの静的な型チェックは困難。うっかり間違った型にダウンキャストして実行時エラーとなる可能性がある。
- RTTI(実行時型情報)を使うと、パフォーマンス低下。
ジェネリックプログラミングで新たに発生する問題 †
- モジュールのインスタンス化の範囲が不明瞭で、型違いモジュールのバイナリを大量生成する場合がある。(直交性の問題。)
- 使い方が複雑すぎて可読性が下がる場合がある。
- 型のネストが極端に長くなって意味不明になる場合がある。
- Foo<Bar<Baz<Hoge<X<Y<Z>>>>>> のような… (実際 C++ のテンプレートでは 256文字をあっさり超える場合がある。)
- ソースでの展開しかできない場合がある。ソースを見せたくない場合にはそぐわない。
- 複雑な型推論が必要とされる場合がある。
- 型に対する汎用性を高めると、結局何もできないことがある。
- 相変わらず RTTI 頼りになることがある。