後回しにしてきたC#のラムダ式、最初の一歩をスタートさせよう


この記事を読むと以下の3つのことがわかります。

・C#言語のラムダ式とは何か?
・C#ラムダ式のコードの書き方
・ラムダ式は自動で型が推測できること

C#言語のラムダ式とは何か?

C#に携わっていると時々耳にするラムダ式という名前。気になってはいたものの、一体何のことなのか?良く分からない人、使いこなせていない人、意外と多いのではないでしょうか。ラムダ式はそもそもC#に限って使われているものではありません。JavaやPythonなどでも利用されています。今回はその中からC#で使えるラムダ式についてご紹介します。

C#のラムダ式は、関数をよりシンプルにコードで表せるようにとマイクロソフトがC#3.0から導入しました。マイクロソフトはデリゲートやLINQなど複雑になってしまいがちな式が、できるだけ簡潔に書けるようにラムダ式を取り入れたのです。ラムダ式が使えるようになったことで、関数のコードはとても短くなりました。

どれくらい短くなったのかというと、例えばラムダ式が導入される前は次のような関数があったとします。

これをラムダ式に変更すると、

たった1行になりました。明らかに分かりやすく短くなったのです。

Λ(ラムダ)は古代ギリシア語に由来していた?

ラムダ式という名前は一体どこから来たのか?実は古代ギリシャ語のギリシャ文字の中にはΛ(ラムダ)というものがありました。C#のラムダもこのギリシャ文字から引用されたといわれています。意味は何かというと、特に存在するわけではなく、関数を現す文字としてxやyとかぶらなくて分かりやすかったから採用され使われるようになったそうです。※注1

C#ラムダ式のコードの書き方

ラムダ式のコードの書き方は2種類あります。いずれの式も左の式には入力パラメーター、右の式には式もしくはステートメントブロックを指定します。

1種類目のラムダ式、①式形式のラムダ(式本体に式を含む書き方)

右側に式があるタイプを式形式のラムダと呼びます。見たまま、式のままの結果を返すシンプルな動きをします。

2種類目のラムダ式、②ステートメント形式のラムダ(式本体にステートメントブロックを含む書き方)

ステートメントが「中かっこ{}」で囲まれているタイプをステートメント形式のラムダと呼びます。ステートメント形式のラムダは任意の数のステートメントで構成できますが、マイクロソフトは2~3個以下を推奨しています。

式木(Expression tree)のラムダ式の書き方

関数といえば式木(Expression tree)形式でコードを書くことが多いと思いますが、この式木(Expression tree)がC#のラムダ式になるとどうなるのか?今回は一般的な関数がどのように式木のラムダ式になるのか、書き方をご紹介します。

まず最初に一般的な数式を作りました。1+2×3=7という式です。

これに加算演算子(+)にはAdd関数を、乗算演算子(*)にはMultiply関数を当てはめます。

次に改行とインデントを追加します。すると次のようなコードが出来上がります。

その次は上の式をC#のコードに変更します。

C#にすると一気にコードが複雑になるのが分かります。これをC#のラムダ式にすると…!

まさかのたった1行、一気にシンプルな形になりました。プログラマだったら何行もコードを書くよりも断然ラムダ式の方が楽なはずです。この式を実行するにはデリゲートへコンパイルする必要があるため、実際にはもうひと手間掛かりますが、ラムダ式は本当にシンプルにコードを作成できます。

ラムダ式は自動で型が推測できる

ラムダ式はなぜここまで省略できるのでしょうか?実は一般的な関数表記と比べてラムダ式には次のようなルールが設定されているのです。

・関数名はつけない(つけることができない)
・引数の型が省略できる
・返り値の型が省略できる

ラムダ式が導入される前は関数名をいちいち定義しなくてはいけませんでしたが、ラムダ式が導入された後は、ただその場でインスタント的に動くコードになればいいという感覚になりました。無駄な式はできるだけ省きたいというプログラマたちの長年のニーズが形になったというわけなのです。

ラムダ式ではint xはxと表記する

一般的な関数ではint xという書き方をしますが、ラムダ式の場合はただxと表記します。返り値はさらに大幅な省略が施されており、(int Add()という型が丸々省略できます。

なぜC#のラムダ式は型が省略できるのか?それはC#に型推論という機能が備わっているからです。コンパイラがラムダ本体やパラメータの型などから自動的に型を推測できます。

コンパイラが型を推測するためのルールを守ろう

ラムダ式はラムダ本体やパラメータの型などからコンパイラが型を推測できるため、型を指定する必要がありません。IEnumerable を問い合わせた場合、入力変数はCustomerオブジェクトだと推測し、メソッドとプロパティにアクセスできます。

しかし次のルールを守らなければコンパイラは推測不可能と判断、エラー値を返すので注意して下さい。

・式にはデリゲート型と同じ数のパラメータを含める
・各入力パラメータはデリゲートパラメータに変換できる形で書く
・戻り値がある場合はデリゲートの戻り値の型に変換できる形で書く

マイクロソフトは”ルールを守る”という新たな仕様をラムダ式に組み込むことで、暗黙の了解が使われるようになり、C#のラムダ式は型を推測できるようになりました。複雑なコードを書かずに欲しいデータが抽出できるようになったのです。

ラムダ式はデリゲート型に変換することができる

作成したラムダ式が、

・値を返さなかった場合→Actionデリゲート型に変換
・値を返す場合→Funcデリゲート型に変換

することができます。例えば2つのパラメータがあるとします。

・値を返さないラムダ式はAction デリゲートに変換
・一つでもパラメータがあり値を返す場合はFunc デリゲートに変換

されます。具体的に例を挙げてみましょう。xという名前のパラメータを指定し、xの二乗の値を返すラムダ式 x => x * x をデリゲート型の変数に当てはめるとどうなるのか?次のような式ができます。

上の式は5の二乗を計算することになるので結果は25が表示されます。ラムダ式は、上記以外でもデリゲート型または式ツリーのインスタンスを必要とするすべてのコードで使えます。今度はC#のラムダ式でLINQを作成するとどのような式になるか?次のような式ができます。

上の式は2,3,4,5の二乗をそれぞれ計算することになるので結果は4,9,16,25が表示されます。LINQ to Objects、LINQ to XML などSystem.Linq.Enumerable クラスでラムダ式を使う場合は、Enumerable.Select メソッドが呼び出されてパラメータはデリゲート型 System.Func となります。LINQ to SQL などSystem.Linq.Queryable クラスを使う場合は、Queryable.Select メソッドが呼び出され、パラメータ型は式ツリー型 Expression> になります。どちらの場合もパラメータ値を指定できます。※注2

最初は覚えることが多いかもしれませんが、流れはいずれもシンプルなので慣れてしまえばとても簡単です。

まとめ

これまでラムダ式という名前を聞いただけで理解するのが難しそうだと敬遠していた方でも、今回の内容を読めば、これからは使いこなすことができるかもしれないと心変わりしたのではないでしょうか?マイクロソフトが提案する最新バージョンや新機能はとにかくシンプルでより使いやすいものばかりが揃っています。一から覚え直す作業は億劫になりがちですが、ラムダ式の使い勝手は重い腰を上げるだけのメリットがあるかもしれません。




エンジニア募集中

株式会社キャパでは中途エンジニアの募集をしています。
私たちと一緒に働きませんか?
募集概要について詳しくは


■参考文献
注1
https://ja.wikipedia.org/wiki/%CE%9B

注2
https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/lambda-expressions

関連記事一覧