はじめに、ラムダ式を学ぶ理由について。
ラムダ式は .NET Framework、Java、TypeScript、Python、PHP などなど各言語で一般的に導入されている記述方式です。新人の方で(時にはベテランの方も)理解できていないエンジニアが多いようですが、一般的な記述方式なので、携わっているプロジェクト使われているかは別として、しっかり身に着けておきましょう!
I ラムダ式を学ぶ前に
デリゲートという概念をご存知でしょうか?知っている方も話の流れ的に読み飛ばさずお付き合いください!デリゲートのイメージを端的に表すと、「メソッドAの引数にメソッドXを渡して、Aの中で引数に渡されたXを使いたい。Xは型が正しければどんなものでもいいよ」というもの。
何を言っているかさっぱりですね。実際にデリゲートを使った実装を行ってみましょう。まず【1】~【3】の実装を見てください。
///// コード【1】 /////
public bool Is3(int n) {
return n == 3;
}
引数で渡ってきた数値nが3か否かを判別するメソッド。簡単ですね。
///// コード【2】 /////
public bool Is5(int n) {
return n == 5;
}
引数で渡ってきた数値nが5か否かを判別するメソッド。簡単ですね。
///// コード【3】 /////
public delegate bool Judgement(int value);
public int CountArray(int[] numbers, Judgement judge) {
int count = 0;
foreach (var n in numbers) {
if (judge(n))
count++;
}
return count;
}
この実装にデリゲートを使っています。2行目をみてください。「Judgement」なるものが定義されていますね。この「Judgement」はdelegateという特殊な型を表しており、このJudgement型で定義した変数には「int型を引数に、bool型を返すようなメソッド」を代入することができるのです。3行目のCountArrayの引数にご注目ください。ほら「Judgement judge」って書いてます!
Judgement型の変数というか引数というか細君というか、、、judgeが定義されていますね!4行目以降のロジックは、さほど難しくはないですね?分からない方もいるでしょう。そのような方は、考えるな。感じろ。の精神で読み進めましょう。
さてさて、上記【1】~【3】を使って、「とある数値配列の中に、3はいくつ含まれているか、5はいくつ含まれているか、を表示する」処理【4】を実装していきます。
///// コード【4】 /////
public void Disp3() {
var numbers = new[] {5, 1, 2, 6, 4, 3, 75, 5, 4, 6};
Judgement judge = Is3;
var count = CountArray(numbers, judge);
Console.WriteLine(count);
}
// Console.WriteLine(count)の結果[1]
public void Disp5() {
var numbers = new[] {5, 1, 2, 6, 4, 3, 75, 5, 4, 6};
var count = CountArray(numbers, Is5);
Console.WriteLine(count);
}
// Console.WriteLine(count)の結果[2]
【4】の4行目を見てください。Judgement型の変数judgeにIs3メソッドを代入して、5行目でCountArrayメソッドの引数にjudgeを渡しています。次に、11行目を見てください。こちらは、引数に直接Is5を渡していますね。これでデリゲートの使い方のメンタルモデルができてきたのではないでしょうか?
ではでは、【3】のメソッドを使って、とある数値配列の中に偶数がいくつ含まれているか、という処理を実装してみましょう。【5】の足りない部分を補ってください。
///// コード【5】 /////
public void DispEven() {
var numbers = new[] {5, 1, 2, 6, 4, 3, 75, 5, 4, 6};
//ここに処理を書いてね!
}
public bool IsEven(int n) {
//ここに処理を書いてね!
}
はい、難しかったですか?
え?答え?答えはあなたの心の中にありますよ。
デリゲートがどんなものかわかっていただけましたでしょうか。では本題のラムダ式に移りましょうと言いたいところですが、もう一つ、「匿名メソッド」についても知っておきましょう。
II 匿名メソッド
匿名メソッドとは、かなり端的に表すと「共通化するまでもない簡単な処理を、メソッド化せず記述できるもの」です。
そもそも、I で出てきたメソッド、【1】と【2】ってメソッド化したくないですよね?例えば何らかのバグにより【4】のDisp3やDisp5メソッドをデバッグで止めて一つ一つ処理を確認しながら進める時、(いや、そんな時あってたまるか!という突っ込みは置いといて)メソッド化していることで、ステップインしなければなりませんね?ステップインしてみたら、
いやこれだけかい!
とあなたは声を荒げることでしょう。
というわけで、本題、このステップインする必要が無い書き方を実現してくれるのが、匿名メソッドなのです。Disp3を例に具体的に記述してみますね。
///// コード【6】 /////
public int CountArray(int[] numbers, Predicate<int> judge) {
int count = 0;
foreach (var n in numbers) {
if (judge(n))
count++;
}
return count
}
public void Disp3() {
var numbers = new[] {5, 1, 2, 6, 4, 3, 75, 5, 4, 6};
var count = CountArray(numbers, delegate(int n) {return n == 3;});
Console.WriteLine(count);
}
まず、【6】の2行目にPredicateという記述がありますね。これはジェネリック版のデリゲートです。Predicate型を使えば、自分でdelegateの型を定義する必要がないのです。(因みにジェネリックとは、さまざまな型に対応するために、型をパラメータとして与えて、その型に対応したクラスや関数を生成する機能です。わからない方は考えずに感じるか、ググってみてください。)
そして、「delegate(int n) {return n == 3;}」の部分が”匿名メソッド”(名前のないメソッド)です。nが3か否かを判断していることがパッとわかって良いですね。
しかし、この匿名メソッド部分、まだ冗長に感じませんか?”n == 3″という条件を伝えたいだけなのに、delegateとかいるの?とか。そのようなクレームに応えたのがラムダ式なのです。では、やっとですが、本題のラムダ式に移りましょう。
III ラムダ式の記述ルール
【6】の
var count = CountArray(numbers, delegate(int n) {return n == 3;})
をラムダ式を用いて書き換えてみます。
var count = CountArray(numbers, n >= n == 3);
いや簡潔にはなったことはなったけど、、、どうしてこうなるの?と思ったことでしょう。
最も冗長なコードから簡潔になっていく様を見ていきましょう。
///// コード【7】 /////
public void Disp3() {
var numbers = new[] {5, 1, 2, 6, 4, 3, 75, 5, 4, 6};
Predicate<int> judge =
(int n) => {
if (n == 3)
return ture;
else
return false;
};
var count = CountArray(numbers, judge);
Console.WriteLine(count);
}
5行目から10行目が最も冗長なラムダ式のコードです。delegateというワードが消えて、代わりに =>(ラムダ演算子)が使われています。=>の左側が引数を宣言している部分で、右側がメソッドの本体です。ラムダ式は一種のメソッドと認識してもらってよいでしょう。このラムダ式が変数judgeに代入されています。代入されるだけで処理が実行されるわけではないことに注意してください。変数judgeへの代入は省略できそうですね。省略してみましょう。
///// コード【8】 /////
var count = CountArray(numbers,
(int n) => {
if (n == 3)
return ture;
else
return false;
};
)
returnの右側には式を書くことができ、n == 3 はbool値を返すことからif文は省略できそうです。
///// コード【9】 /////
var count = CountArray(numbers, (int n) => {return n == 3;});
引数の型はコンパイラが正しく推論してくれるので省略可能ですね。
ラムダ式の{}内が1文の場合、{}とreturnが省略可能というルールがあります。
また、引数が1つの場合は引数の()も省略可能です。
以上より下記の最終形態になります。
///// コード【10】 /////
var count = CountArray(numbers, n >= n == 3);
では、【11】をラムダ式を用いて簡素に書き換えてみてください。
///// コード【11】 /////
public void DispGoigoisu() {
var words = new[] {"Goigoisu", "Rsh", "Quhgs", "Goigoisu", "Pyhsj"};
Predicate<string> judge =
(string n) => {
if (n == "Goigoisu")
return ture;
else
return false;
};
var count = CountArray(words, judge);
Console.WriteLine(count);
}
// Console.WriteLine(count)の結果[2]
var count =//ここに処理を書いてね!
できましたでしょうか?さほど難しくないですね。これでラムダ式がどんなものか感じていただけたのではないでしょうか。
終わりに、ラムダ式ってどんなところで使うの?
そもそもラムダ式の何がいいのか、どんな時にラムダ式の記述が活きるのか。
一番は、上記のように処理を明確に、そして簡潔に記載できることです。
そういえば、ListクラスやLINQなどラムダ式を使ったメソッドが.NET Frameworkに豊富に用意されていますね。ListクラスやLINQは.NETにおいて大変重要で便利な技術ですので興味のある方は学習してみてください。
テックマートの社員はこれを機にぜひ学びを深めましょう!
定時後や休日に出社して勉強するエンジニア。
現場業務を持ちながらも営業に同行するなどプラスワンのエンジニア。
クラサバエンジニアから希望するWebエンジニアに華麗に転身しました。
愛読書は「アジャイルサムライ」。