第一級オブジェクト
第一級オブジェクト(ファーストクラスオブジェクト、first-class object)は、あるプログラミング言語において、たとえば生成、代入、演算、(引数・戻り値としての)受け渡しといったその言語における基本的な操作を制限なしに使用できる対象のことである。ここで「オブジェクト」とは広く対象物・客体を意味し、必ずしもオブジェクト指向プログラミングにおけるオブジェクトを意味しない。第一級オブジェクトは「第一級データ型に属す」という。
この言葉は1960年代にクリストファー・ストレイチーによって「functions as first-class citizens」という文脈で初めて使われた。
言語によって異なるが、第一級オブジェクトは概ね次のような性質をもつ。
- 無名のリテラルとして表現可能である。
- 変数に格納可能である。
- データ構造に格納可能である。
- それ自体が独自に存在できる(名前とは独立している)。
- 他のものとの等値性の比較が可能である。
- プロシージャや関数のパラメータとして渡すことができる。
- プロシージャや関数の戻り値として返すことができる。
- 実行時に構築可能である。
- 表示可能である。
- 読み込むことができる。
- 分散したプロセス間で転送することができる。
- 実行中のプロセスの外に保存することができる。
例えば、C言語やC++では、スカラー型[注釈 1]と構造体(C++の場合はクラス型として総称される)の値は代入演算=
で使用でき、関数の引数や戻り値の型として直接使用することもできる(つまり関数に値を渡したり関数から値を返したりすることもできる)。これらは第一級オブジェクトであるといえる。しかしC/C++では、配列(言語組み込みの固定長配列)は、配列のデータ型のまま代入することもできなければ、関数の引数や戻り値の型として直接使用することもできない。配列そのものではなく、ポインタもしくは参照[注釈 2]を用いる必要があるが、ポインタおよび参照の代入は配列自身の代入ではなく、あくまでポインタや参照自身のコピーにすぎない。配列を構造体(またはクラス)に埋め込み、その構造体の値を代入演算で使用することや、構造体を関数の引数や戻り値の型とすることはできるが、この場合、代入や関数との受け渡しが扱っているデータ型はあくまで構造体であり、配列そのものではない[3]。配列の各要素についてはそれらの操作ができるが、配列全体をひとつとして扱うことはできない。そのため、C/C++の配列は、第一級オブジェクトではない。C/C++の文字列は文字配列中のヌル文字を使って終端を表すヌル終端文字列であるが、配列を使って実現されていることから、やはり第一級オブジェクトではない。また、C/C++での関数は実行時に作成することができない。したがってC/C++では関数も第一級オブジェクトではない。ただし、関数ポインタを使用することで上の性質の多くを満たすことができるため、C/C++の関数を第二級オブジェクトという場合がある。
FORTRAN 66における文字列は変数に格納することができないため、第一級オブジェクトではない。
Smalltalkでは、無名関数(ブロック)については他言語同様として、関数(メソッド)もクラスと同じように第一級オブジェクトである(CompiledMethodクラスのインスタンス。実体はバイトコード列でクラスのメソッド辞書に値として格納されている)。演算子(+
、-
)もSmalltalkではメソッドで実現されているため、他の通常のメソッド同様やはり第一級オブジェクトである。
ほぼすべての言語において、整数や浮動小数点数などの最も単純なデータ型は第一級オブジェクトである。一方、歴史の古い言語のうち、C/C++のように機械語に近い設計思想(ゼロオーバーヘッド原則)を持つ言語においては、配列や文字列は第一級オブジェクトではなかった。それらはオブジェクトとして例えば直接代入することはできず、その要素のみを個別に扱うことしかできなかった。これは計算資源が貧弱で、コンパイラの最適化能力も低かった時代の名残でもある。配列や文字列の自己完結のために必要なバッファの長さ情報を格納するための領域確保すらも当時は贅沢であり、徹底したメモリの節約が必要だったからである。
脚注
[編集]注釈
[編集]- ^ Cの場合、整数型や浮動小数点数型といった基本型と、ポインタ型の総称[1]。C++の場合は厳密な定義は異なるが、概ねCに準ずる[2]。
- ^ C/C++では、固定長配列へのポインタ
T(*)[N]
であれば引数または戻り値の型として使用することはできる。C++の場合は固定長配列への参照T(&)[N]
であれば引数または戻り値の型として使用することはできる。単純な要素型へのポインタT*
を使って配列内の特定位置にある要素のアドレスを受け渡しすることができ、また配列の要素がメモリ上で連続していることを利用して任意の位置にある要素にポインタのオフセット演算だけでアクセスできるので、任意のサイズの配列を受け渡しする際にポインタが利用される(通例配列先頭要素のアドレスが使用される)。ただし、配列のサイズは別途受け渡しするか仮定する必要がある。
出典
[編集]- ^ 型 - cppreference.com (C)
- ^ 型 - cppreference.com (C++)
- ^ “C Program For Create An Array In Structure”. Programming With Basics (2016年4月29日). 2023年11月8日閲覧。