※この文章はベータ版です。
C/C++
C/C++は、型ありき(type first)で設計されています。
型は前置です。
変数宣言時に必ず変数と型を紐付けし、変数の初期化時に型に合わせて、メモリを
確保します。
Cは、設計段階で、型をつけた変数を定義して、変数間で値をコピーして渡す「値渡し」
が先ずあり、「値渡し」だけでは常に値のコピーが発生し、パフォーマンスが
でないので、値がある場所(アドレス)を指し、値の直接操作の可能な「ポインタ」が
導入された、感じがします。
Cはポインタに独自の型(ポインタ型)を用意せず、型に対して*を後置することで
ポインタ型を表現しています。
(ポインタ変数は、アドレスの入れ物なので、ポインタ変数間で「値渡し」できます。
ポインタ変数に「*」を付けることで、ポインタ変数に格納したアドレスにある値を
直接操作できます。)
(ポインタはアドレスの入れ物なので、ポインタ自体は格納されているアドレスに
ある変数の型の情報を必要とはしていませんが、実装上、ポインタの指すアドレスに
ある変数の型がわからないと、その変数内にある値にアクセスできないため、変数自体
には自分の型の情報を持たせず、ポインタ変数の宣言時にポインタ変数にポインタの
指す変数の型を紐付けるか、変数自体に自分の型の情報を持たせておき、ポインタで
アクセスするとポインタの指す変数から取得できるようにするか、する必要があります。
静的型付言語であるCでは前者を採用しています。)
ポインタの説明で、ポインタ型のロジックでの説明はあまり見かけず、
(値)型とポインタ型の区別がつきにくく、ポインタの理解がしにくく
なってしまってきた気がします。
ポインタ型変数とは
(ポインタ変数・通常変数モードというロジックでの説明は。。。)
ポインタ
(ポインタ変数は、宣言が「型* 変数名;」だけでなく「型 *変数名;」とも
書けることで、変数名に*をつけて宣言する変数で、変数名でアドレスを
格納できる、風の誤解を誘発してしまっている気がします。)
C++では、ポインタに加えて、制約はあるものの値を参照して直接操作できる「参照」が
新たに導入されました。
参照も、独自の型(参照型)を用意せず、型に対して&を後置することで参照型を
表現しています。
参照の説明でも、参照型のロジックで説明したほうが、理解しやすいのではないか
と思います。
C/C++には関数ポインタがあるので、変数経由で関数・メソッドを操作することが
できます。
(関数ポインタ(変数)の宣言は、変数風の「(返値の型 (引数の型))* 変数名;」の
ようには書けず、関数風に「返値の型 (*変数名)(引数);」と書きます。)
c++はポインタと参照の2つがあるので、ややこしいです。
Swift
Objective-Cの影響を強く受け、設計されています。
型は後置です。
変更宣言時の変数への型付けはしてもしなくてもよく、していない場合、変数の
初期化時に代入した値の型により、メモリが確保されると共に、変数の型が
決まります。(但し、初期化時に代入値がnilの変数は宣言時の型付が
必須です。)
ポインタはあります。が、C言語との互換性などでどうしても必要な場合だけに
利用すべきモノです。
C/C++のようなポインタありきの言語、ではありません。
ポインタを表すポインタ型が数種あります。
(ポインタはARC対象外なので、メモリ管理する必要があります。)
構造体と列挙型は値型で値渡し、
クラスは参照型で参照渡しです。
(関数・メソッドの引数はデフォルトでは定数なので、引数として代入した変数を
変更するには、型の前にinoutキーワードをつけなくてはなりません。
(値型の変数であっても、inoutが付いた引数に代入すると、関数内で元の変数を変更
できます?)
クラス・構造体の値型のプロパティをインスタンスメソッドから変更するには、
メソッド定義の際にfuncキーワードの前にmutatingキーワードをつけなくては
なりません。)
関数型があるので、関数・メソッドは変数経由で呼び出すことができます。
(Swiftの参照渡しには、C++の参照にあるような制約はありません。)
Java
C/C++の影響を強く受け、型ありき(type first)で設計されています。
型は前置です。
変数宣言時に必ず変数と型を紐付けし、変数の初期化時に型に合わせて、
メモリが確保されます。
ポインタはありません。
プリミティブな型(int、float etc)は値渡し、
それ以外の型は参照渡しです。
関数型がない(メソッド(関数)はオブジェクトではない)ので、メソッド(関数)を
変数経由で呼び出すことはできません。
(Javaの参照渡しには、C++の参照にあるような制約はありません。)
JavaScript
動的型付けを採用しています。
変数は型を付けずに定義し、いかなる型も代入できます。
(型がないわけではなく、代入する値の型で変数の型が決まります。)
プリミティブな型(int、float etc)は値渡し、
それ以外の(オブジェクト)型は参照渡しです。
関数(クロージャ含む)・メソッドはオブジェクトなので((関数型Functionが
あるので)、関数・メソッドも変数経由で呼び出すことができます。
Ruby
Rubyは動的型付けを採用しています。
変数は型を付けずに定義し、いかなる型も代入できます。
(型がないわけではなく、代入する値の型で変数の型が決まります。)
全ての型は参照の値渡しです。
変更不能(immutable)な型の場合は値渡し(ライクな挙動)です。
変更不可(immutable)な型
- 数値型
- シンボル型
関数・メソッドはオブジェクトではない(関数型もない)ので、関数・メソッドを
変数経由で呼び出すことはできません。
クロージャ(proc、lambda)はオブジェクトなので(Proc型であるので)、変数
経由で呼び出すことができます。(クロージャは関数の一種です。)
Python
Pythonは動的型付けを採用しています。
変数は型を付けずに定義し、いかなる型も代入できます。
(型がないわけではなく、代入する値の型で変数の型が決まります。)
全ての型は参照渡しです。
(関数・メソッドの引数は、変更可能(mutable)な型の場合、参照渡し、
変更不能(immutable)な型の場合は値渡し(ライクな挙動)です。)
変更不可(immutable)な型
- int, float, complexといった数値型
- 文字列型(string)
- タプル型(tuple)
- bytes
- Frozen Set型
変更可能(mutable)な型 - リスト型(list)
- バイト配列(bytearray)
- 集合型(set)
- 辞書型(dictionary)
関数・メソッドはオブジェクトなので(関数型Functionがあるので)、関数・メソッド
を変数経由で呼び出すことができます。
最後に
今後、他の言語を追加するかもしれません。