型 推論
ただし
具体 例 による説明
この
ほとんどの
int addone(int x) {
int result;
result = x + 1;
return result;
}
int addone(int x)
では、addone
はint result;
のresult
が
let addone (x : int) : int =
let result : int = x + 1
result
しかしF#は
let addone x =
let result = x + 1
result
このF#の
- 2
項 演算 子 +
の左 オペランドと右 オペランドの型 は同 じであり、演算 結果 は同 じ型 を返 す。
という1
がx
すなわちx
もx + 1
のresult
のaddone
let y1 = addone 3
let y2 = addone 3.0 // double 型 の値 を渡 すと、型 の不一致 によりコンパイルエラー。
let y3 = addone 3y // sbyte 型 の値 を渡 すと、型 の不一致 によりコンパイルエラー。
なお、
型 推論 のバリエーション
変数 宣言 時 の型 推論
C#はバージョン3.0にて、var
キーワードを
// 型 推論 を用 いない書 き方 。
string s1 = "文字 列 ";
System.Console.WriteLine(s1.GetType()); // System.String
// 型 推論 を用 いた書 き方 。
var s2 = "文字 列 ";
System.Console.WriteLine(s2.GetType()); // System.String
var now = System.DateTime.Now;
System.Console.WriteLine(now.GetType()); // System.DateTime
now = s2; // コンパイルエラー。
var action1 = () => {}; // コンパイルエラー。
var action2 = delegate() {}; // コンパイルエラー。
var action3 = System.GC.Collect; // コンパイルエラー。
var action4 = new System.Action(() => {});
System.Action action5 = () => {};
var dict = new System.Collections.Generic.SortedDictionary<string, int> { {"Bravo", 0}, {"Alpha", 1}, {"Charlie", 2} };
foreach (var entry in dict) {
//System.Diagnostics.Debug.Assert(entry is System.Collections.Generic.KeyValuePair<string, int>);
System.Console.WriteLine("Key={0}, Value={1}", entry.Key, entry.Value);
}
この
ラムダ
// ラムダ仮 引数 の型 推論 を用 いない書 き方 。
System.Func<double, double> func1 = (double x) => x * x;
// ラムダ仮 引数 の型 推論 を用 いた書 き方 。
System.Func<double, double> func2 = (x) => x * x;
Javaはバージョン8にてラムダvar
によるローカルvar
を
C++はC++11auto
およびdecltype
による
namespace {
auto g_variable = 0.0; // double
struct MyType {
static const auto s_variable = 0L; // long
};
}
int main() {
auto n = 0; // int
decltype(n)* p1 = &n; // int*
decltype(&n) p2 = &n; // int*
decltype(n)& r1 = n; // int&
decltype((n)) r2 = n; // int&
auto f = []() {}; // コンパイラが生成 する関数 オブジェクト(クロージャ)型 。
}
戻 り値 の型 推論
C++はC++11auto
およびdecltype
によるdecltype(auto)
による
#include <iostream>
template<typename TFunc, typename TArg> auto invokeFunc(const TFunc& f, const TArg& a) -> decltype(f(a)) {
return f(a);
}
int main() {
std::cout << invokeFunc([](double x) { return x * x; }, 1.4142) << std::endl;
}
総称 型 の型 推論
C++では、
#include <iostream>
#include <cmath>
template<typename T> T getVectorLength(T x, T y, T z) {
return std::sqrt(x * x + y * y + z * z); // std::sqrt() には double あるいは float を受 け取 るオーバーロードが存在 する。
}
int main() {
const double len1 = getVectorLength<double>(1, 2, 3); // double getVectorLength(double, double, double)
const float len2 = getVectorLength(1.0f, 2.0f, 3.0f); // float getVectorLength(float, float, float)
std::cout << len1 << std::endl;
std::cout << len2 << std::endl;
}
テンプレートT
をT&
あるいはポインタT*
であったり、ユニバーサルT&&
であったりする
C++17ではクラステンプレートのテンプレート
template<typename T> struct Vector3 {
T x, y, z;
Vector3(T ax, T ay, T az) : x(ax), y(ay), z(az) {}
};
int main() {
Vector3<double> v1(1.0, 2.0, 3.0); // C++03 以前 でも利用 可能 な、従来 のコンストラクタ呼 び出 しによる実体 化 。
Vector3<double> v2 { 1.0, 2.0, 3.0 }; // C++11 以降 の uniform initialization を使用 した実体 化 。
Vector3 v3(1.0, 2.0, 3.0); // C++17 以降 でのみ有効 。Vector3<double> に推論 される。
Vector3 v4 { 1.0, 2.0, 3.0 }; // 同上 。
}
Javaはバージョン5.0
// 型 推論 を用 いない書 き方 。
List<String> list1 = Collections.<String>emptyList();
// 型 変数 へのバインドに型 推論 を用 いた書 き方 。
List<String> list2 = Collections.emptyList();
その
// 型 推論 を用 いない書 き方 。
List<String> list1 = new ArrayList<String>();
// ダイヤモンド演算 子 による型 推論 。
List<String> list2 = new ArrayList<>();
無名 関数 の型 推論 の例
C#の
// 複数 のデリゲート型 を定義
delegate void TwoStringAction(string left, string right);
delegate void OneParamAction(object o);
delegate void TwoParamAction(object o, EventArgs e);
delegate void TwoIntegerAction(int x, int y);
// メソッドのオーバーロードを用意 する。有効 化 するオーバーロードの種類 により、型 推論 の可否 が変化 する。
static void SomeMethod(TwoStringAction action) { /* Pattern 1 */ }
//static void SomeMethod(OneParamAction action) { /* Pattern 2 */ }
//static void SomeMethod(TwoParamAction action) { /* Pattern 3 */ }
//static void SomeMethod(TwoIntegerAction action) { /* Pattern 4 */ }
static void Main() {
// メソッドのオーバーロードがPattern 1のみの場合 、全 ての文 が有効 (型 推論 可能 )である。
SomeMethod((o, e) => { /*No-op*/ }); /* 1行 目 */
SomeMethod((o, e) => { o = o + e; }); /* 2行 目 */
SomeMethod((o, e) => { o = "" + o + e; }); /* 3行 目 */
SomeMethod(delegate { /*No-op*/ }); /* 4行 目 */
}
SomeMethod(OneParamAction action)
の行 を有効 にすると、Mainメソッドの4行 目 の型 推論 が効 かなくなる。- 4
行 目 の匿名 関数 は引数 リストを省略 できるため、1引数 、2引数 のどちらのデリゲート型 か曖昧 になる。
- 4
SomeMethod(TwoParamAction action)
の行 を有効 にすると、Mainメソッドの2行 目 以外 の型 推論 が効 かなくなる。- 1
行 目 のラムダは、2引数 で値 を返 さない複数 のデリゲート型 がある場合 、曖昧 となる。 - 3
行 目 のラムダは、代入 式 の左辺 にあるo
はstring
を格納 できる型 でなければならないが、一方 で右辺 のo
とe
は暗黙 にToString()
が呼 び出 されるため、いずれの型 であってもよく、TwoStringAction
TwoParamAction
の間 で曖昧 となる。 - 2
行 目 のラムダは、o
とe
の間 に適切 なoperator+
が定義 されなければならず、TwoStringAction
型 と推論 される。
- 1
SomeMethod(TwoIntegerAction action)
の行 を有効 にすると、Mainメソッドの3行 目 以外 の型 推論 が効 かなくなる。- 2
行 目 のラムダは、o
とe
はstring
かint
のいずれでもoperator+
が実行 できるため、TwoStringAction
TwoIntegerAction
の間 で曖昧 となる。 - 3
行 目 のラムダは、代入 式 の左辺 にあるo
はstring
を格納 できる型 でなければならないため、TwoStringAction
型 と推論 される。
- 2
デリゲート
動的 型 言語 における型 推論
JavaScript では、Webブラウザの
Groovy 2.0 ではコンパイル@TypeChecked
の
脚注
- ^ C
言語 は関数 の戻 り値 の型 を省略 した場合 、int
を返 すと仮定 する仕様 になっているが、これを型 推論 とは呼 ばない。 - ^
暗黙 的 に型 指定 されるローカル変数 - C# プログラミング ガイド | Microsoft Docs - ^ auto - cpprefjp C++
日本語 リファレンス - ^ decltype - cpprefjp C++
日本語 リファレンス - ^ クラステンプレートのテンプレート
引数 推論 - cpprefjp C++日本語 リファレンス - ^ a b Type Inference brings JS improvements to Firefox Beta Brian Hackett, 2011
年 11月 10日 (2011年 12月24日 閲覧 )。型 推論 により Firefox Beta の JavaScript が高速 化 しました(上 の記事 の和訳 )、2011年 12月24日 閲覧 。 - ^
一色 政彦 (2011年 4月 22日 ). “Internet Explorer 9正式 版 レビュー”. @IT. December 24, 2011閲覧 。 - ^ “Mozilla、「Firefox 9」の
正式 版 をリリース 「型 推論 」技術 で45%高速 に”. ITmedia (2011年 12月21日 ). December 24, 2011閲覧 。