ラムダ式

ラムダ式とは

ラムダ式は、前ページで説明したデリゲートをより簡単に使用できるものです。
ラムダ式は式ツリー(式木)を作成するためにも用いられますが、こちらはかなりマニアックというか普通ではないプログラミングの手法なので置いておきます。

匿名メソッド

前ページではdelegateによる匿名メソッドの作り方を説明しました。


//配列から条件に合致する要素を抽出するメソッド
//「条件」は引数のFuncデリゲートで指定できる
static int[] Select(int[] arr, Func<int, bool> func)
{
    //省略
}

static void Main(string[] args)
{
    int[] nums = new int[]
            { 2, 4, 7, 8, 11, 14, 18, 19 };

    int[] arr1 = Select(
        nums,
        //匿名メソッド
        delegate(int n) 
        {
            return n >= 10;
        });
}

この書き方はラムダ式がC#に導入される以前(C#2.0)の方法で、今はほとんど使われていません。
これをラムダ式で書き直すと以下のようになります。


//配列から条件に合致する要素を抽出するメソッド
//「条件」は引数のFuncデリゲートで指定できる
static int[] Select(int[] arr, Func<int, bool> func)
{
    //省略
}

static void Main(string[] args)
{
    int[] nums = new int[]
            { 2, 4, 7, 8, 11, 14, 18, 19 };

    int[] arr1 = Select(
        nums,
        //ラムダ式
        (int n) => {
            return n >= 10;
        });
}

分かりやすいように両者を並べてみます。


//delegateによる匿名メソッド
delegate(int n) { return n >= 10; };

//ラムダ式による匿名メソッド
(int n) => { return n >= 10; };

ラムダ式では「delegate」のようなキーワードは使用しません。
その代わりに、引数リストの次に「=>」という矢印のような記号を使用します。
これをラムダ演算子といい、ラムダ式を定義するという意味になります。


(引数リスト) => {
    ラムダ式の処理
};

ラムダ式はdelegateと同じく、ActionデリゲートFuncデリゲートに代入できます。


//引数なし、戻り値なし
Action action = () => { Console.WriteLine("test"); };

//int型引数ひとつ、戻り値bool型
Func<int, bool> func = (int n) => { return n < 0; };

action(); //「test」を表示
bool b = func(3); //false

定義済みデリゲート(ActionやFuncなど)とラムダ式の導入により、C#ではもはや「delegate」という単語を使用する機会はほとんどありません。

ラムダ式の省略形

ラムダ式のブロック内の処理が一行で済む場合はブロック記号{}を省略できます。
戻り値がある場合は「return」の記述も省略できます。
(というよりreturnの省略は必須になる)


(int n) => { return n >= 10; };

//↓

(int n) => n >= 10;

//使用例
int[] arr1 = Select(nums, (int n) => n >= 10);            

また、引数の型が推測できる場合はこれを省略することができます。


(int n) => { return n >= 10; };

//↓

(n) => { return n >= 10; };

//使用例
int[] arr1 = Select(nums, (n) => { return n >= 10; });            

これはどういうことかと言うと、例に使用しているSelectメソッドが引数に取れるデリゲートは「Func<int, bool>」なので、ここからint型であると推測ができます。
別のデータ型を取るデリゲートにラムダ式を渡せば、ラムダ式の引数のデータ型も自動的にそのデリゲートで指定されているデータ型と推測されます。


static int[] Select(int[] arr, Func<int, bool> func)
{
    //省略
}

static float[] Select(float[] arr, Func<float, bool> func)
{
    //省略
}

static void Main(string[] args)
{
    int[] nums = new int[]
            { 2, 7, 11, 18 };
    float[] reals = new float[]
            { 2f, 7f, 11f, 18f };

    //引数nはint型
    int[] arr1 = Select(nums, (n) => { return n >= 10; });

    //引数nはfloat型
    float[] arr2 = Select(reals, (n) => { return n >= 10; });
}      

さらにこの時、引数がひとつの場合は引数の丸括弧()を省略できます。


(int n) => { return n >= 10; };

//↓

(n) => { return n >= 10; };

//↓

n => { return n >= 10; };

これらの省略形を組み合わせると、ラムダ式は最小で以下のようになります。


(int n) => { return n >= 10; };

//↓

n => n >= 10;

ここまで簡略化した形にすると慣れない間は戸惑うかもしれませんが、ラムダ式は非常に便利なのでぜひマスターしましょう。
別に無理に省略形で書く必要はありませんが、他人が書いたコードを読む場合にはしばしば登場するので理解しておいた方が良いです。


//引数のないラムダ式
//引数がない場合は丸括弧の省略はできない
() => Console.WriteLine("test");

//引数が二つ以上の場合も
//丸括弧の省略はできない
(x, y) => x > y;

//処理が複数行の場合は
//{}とreturnは省略できない
(n) => {
    int r = 0;
    if (n < 0)
        r = -1;
    else if(n > 0)
        r = 1;
    return r;
};

メソッドの定義

ラムダ式は通常のメソッドの処理を定義することもできます。
(C#6.0以降)
ただしここで使用できるのは一行で書けるラムダ式です。
(式形式という)
複数行のラムダ式(つまりブロック{}を使用するもの)は使用できません。


static void Speak() =>  Console.WriteLine("Hello");

static void Main(string[] args)
{
    Speak();
}