FrontPage

メモ

  • 入力、演算、制御、記憶、出力の方法を記述するのがプログラミング言語。
    • 文法がどんなに違っても、5大機能をどうするかを書くことに変わりはない。
    • (例外的なのは Prolog のようなパターンマッチング言語だがここでは対象としない。)
  • 何度も同じことを書かないために、サブルーチン化、関数化、クラス化する。
    • オブジェクト指向では継承などをつかう。
  • よく使うサブルーチンなどを集めたものがライブラリとなる。
  • 関数型言語
    • 関数(ルーチン)内での入出力がない。関数呼び出しの最初に入力を渡し、最後に出力が返る。
    • 記憶や外部からの影響がないため、入力が決まれば出力が必ず決まる。
    • 副作用がない。
    • 複雑度が小さい。
    • 実行順は関係ない。
    • 入力と出力をキャッシュすることができる。
    • 関数型言語の反対は「非関数型言語」であって「手続き型言語」とはちょっと違う。
  • 手続き型言語
    • 手続き(手順)を記述する事を重点にしている。
    • 一般に、人間が考える時は手続きの方が考えやすい。
    • 手続き型言語でもルーチン内での入出力、記憶をなくせば、関数型言語と同じ結果が得られる。

各言語の相違点について

  • 文法が異なる。
    • 宣言の有無
    • オブジェクトの型の有無
    • 変数の型の有無
    • 型チェック機構の有無
    • 数式表現の差異
    • オブジェクト指向表現の有無
  • プログラムがデータとして取り扱えるか。(LISP, eval関数など)
  • データの取り扱いが異なる。(記憶方法、表現方法)
  • ガーベジコレクションの有無
  • ガーベジコレクションの方法の差異
    • 参照数
    • マーク&スイープGC
    • 世代型GC
  • ルーチンへの値の渡し方が異なる。
    • 値渡し 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 頼りになることがある。

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2007-09-17 (月) 08:30:18 (1089d)