ラムダ式とFunc

関数を手軽に使用できるのがラムダ式の良いところである。「少しだけ」関数を利用したいというときに便利。 実際の応用例は次回 LINQ で述べる。

ローカル関数

ローカル関数は、メソッドの中で定義可能な関数をいう。
Program1.

static void Main(string[] args){
    void hello() { Console.WriteLine("Hello"); }
    hello();
}
実行結果. Hello

Action

Action は「void」型の関数を扱うデータ型である。

宣言方法 変数aに代入できるもの
Action a; void型の引数がない関数
Action<int> a; void型のint型引数を1つ持つ関数
Action<int,int> a; void型のint型引数を2つ持つ関数

Program2.

static void Main(string[] args){
    void hello() { Console.WriteLine("Hello"); }
    Action a = hello;
    a();
}
実行結果. Hello

ラムダ式

Program2 をラムダ式によってかきかえたもの.

static void Main(string[] args){
    Action a = () => Console.WriteLine("Hello");
    a();
}
実行結果. Hello

解説.

() => Console.WriteLine("Hello");
の部分がラムダ式で、引数なしのvoid型の関数を意味する。 あたらしい記号「=>」に注目する。 これは「ラムダ演算子」と呼ばれるもの。この演算子の左側に「引数」を 書き、 右側に関数の中身を記述する。今の場合、ラムダ演算子の左側は 丸カッコ「()」のみになっているが、これは、引数がないことを意味する 。

先ほどの例では、引数がなかったのでわかりづらかったかもしれない。今 度は引数がある例をみる。

Action<int> a = (int x) => Console.WriteLine(x);
a(2);
実行結果. 2

解説.

(int x) => Console.WriteLine(x);
の部分がラムダ式で、引数が x の void型 の関数を意味する。 今度は、ラムダ演算子左側に (int x) と引数が記述されている。

C#では「変数は使用する前に宣言が必要」という原則があるが、しかし、 上の例では例外的に、

(int x) => Console.WriteLine(x);
のかわりに「int」を省略して
(x) => Console.WriteLine(x);
と書くことができる。 これは Action <int> の部分で引数がint型であることが明示されているため型が推論できるためだと考えれば良い。

Func

戻り値が必要な関数を変数として扱いたいときは Action ではなく Func を用いる。

Func<int> f; fは引数なしのint型をとる関数
Func<int,double> f; fはint型引数1つで、double型の値をとる関数
Func<int,int,double> f; fはint型引数2つで、double型の値をとる関数

Action とは少し宣言方法が異なるので注意が必要である。 Action の場合はvoid型専用のものなので、わざわざvoidであるという宣言が必要なかったが、 Func は戻り値のデータ型を指定しなければならない。

Func<型1, 型2, ..., 型(N-1),型N> f;

とするとき、型1から型(N-1)までが引数の型で、最後に書かれた型が「関 数 f が返すデータ型」となる。 したがって、上のようにデータ型が N 個あっても、f の引数の個数は(N-1)個となる。

例1.

Func<int, int, bool> f;
f = (x, y) => x > y;
Console.WriteLine(f(3,2));
実行結果. True

例2.

Func<int, int, int> f = (x, y) => x*x - y*y;
Console.WriteLine(f(2,1));
実行結果. 3

例2では、戻り値がint型で、引数もint型の f(x,y) = x*x - y*y という 2 変数関数を定義したことになる。

まとめ

関数を変数に代入して扱うには、Action,Funcを用いる。 今回は理解することを優先したため、ラムダ式の関数定義から名前も付与したのであるが、 ラムダ式のメリットの1つは名前不要であることなので、そのメリットを生かせなかった。 次回はLINQと一緒に利用し、名前不要のメリットを確認する。