初めに
初投稿です。
C#初心者です。仕事で使うため現在勉強中です。
ラムダ式について、LINQとかで形式的に使えるけどなぜそう書くのかよくわかっていなかったので、
以下の書籍を参考に、自分の備忘録(Output)のため記事を投稿しました。
自分が学んだことを記事として投稿(文章化)することで、自分の知識にしようという試みです。
参考書籍:実戦で役立つ c#プログラミングのイディオム/定石&パターン
ラムダ式とは?
C#言語のラムダ式(lambda expressions)とは、デリゲート(delegate)や、メソッド・ベースのLINQ文の(例えば)WhereメソッドやSelectメソッドなどの引数をシンプルに記述するために、C# 3.0(=Visual C# 2008)以降で導入された言語仕様である。
?
僕はよくわかりませんでした。。
そもそもデリゲート(delegate)って?
まずはデリゲートを理解する
C# によるプログラミング入門より引用
デリゲート(delegate: 代表、委譲、委託)とは、メソッドを参照するための型です。
自分はこう解釈しました。
delgate型:〇〇の型で受け取って、××の型で返すメソッドを代入できる型
例えば
delegatebooljudgement(intvalue);
→int型で値を受け取って、bool型の値を返すメソッドを代入することができる
つまるところ、入力と出力さえ合っていいれば中身がどんなメソッドでも代入することができる・・・?
試しに、Consoleから入力した数字について、何らかの判断をしてTrueかFalseを返すプログラムで確認
staticvoidMain(string[]args){intnum=int.Parse(Console.ReadLine());Judgementjudge=メソッド名;Console.WriteLine(judge(num));Console.ReadKey();}publicdelegateboolJudgement(intvalue);
偶数判断のメソッドを追加して、メソッド名のところに”IsEven”を代入すると、
staticvoidMain(string[]args){intnum=int.Parse(Console.ReadLine());Judgementjudge=IsEven;Console.WriteLine(judge(num));Console.ReadKey();}publicdelegateboolJudgement(intvalue);publicstaticboolIsEven(intnum){returnnum%2==0;}
入力した数値が偶数であればTrue,偶数でなければFalesを返します。
奇数判断メソッドを追加して”IsOdd”を代入すると、
staticvoidMain(string[]args){intnum=int.Parse(Console.ReadLine());Judgementjudge=IsOdd;Console.WriteLine(judge(num));Console.ReadKey();}publicdelegateboolJudgement(intvalue);publicstaticboolIsEven(intnum){returnnum%2==0;}publicstaticboolIsOdd(intnum){returnnum%2==1;}
奇数かどうかをTrue、Falseで返してきます。
素数判断メソッドを追加しても
staticvoidMain(string[]args){intnum=int.Parse(Console.ReadLine());Judgementjudge=IsPrime;Console.WriteLine(judge(num));Console.ReadKey();}publicdelegateboolJudgement(intvalue);publicstaticboolIsPrime(intnum){if(num<2)returnfalse;elseif(num==2)returntrue;elseif(num%2==0)returnfalse;// 偶数はあらかじめ除くdoublesqrtNum=Math.Sqrt(num);for(inti=3;i<=sqrtNum;i+=2){if(num%i==0){// 素数ではないreturnfalse;}}// 素数であるreturntrue;}
入力した数字が素数かどうかを判断することができるようになる。
つまり
delegateboolJudgement(intvalue);
で宣言したとしたら、int型の引数でbool型の返り値のメソッドだったらなんでも入るってことですね。
でも、偶数判定とか奇数判定とか1文で終わるようなものもいちいちメソッドを定義してあげないといけません。。煩わ((
→メソッドをわざわざ定義しなくても、メソッドを渡してあげることができれば!
メソッドを定義することなくメソッドを渡してあげる方法
匿名メソッドを利用
publicdelegateboolPredicate<inT>(Tobj);
→Predicateは<T>型を受け取ってbool型を返すメソッドを代入することができる
※Tのところはintでもstringでもなんでもござれ
Predicateを使うことで、わざわざdelegate型を自分で定義して宣言する必要がなくなる!
※ちなみに、Predicate以外にもこんなデリゲートがある
・Action<T> T型を受け取って返り値がない(void)のメソッドを代入可能
・Func<T> T型の返り値を返す、引数がないメソッドを代入可能
ちょっとプログラムを改修して確かめてみる
staticvoidMain(string[]args){intnum=int.Parse(Console.ReadLine());Console.WriteLine(result(num,"偶数",IsEven));Console.ReadKey();}//入力値の判定結果(文言)を返すメソッドpublicstaticstringresult(intnum,stringcategory,Predicate<int>judge){if(judge(num)==true)return$"あなたが入力した数値:{num}は{category}です。";elsereturn$"あなたが入力した数値:{num}は{category}ではありません。";}//偶数ならTrueを返すメソッドpublicstaticboolIsEven(intnum){returnnum%2==0;}
入力した値について偶数かどうかを判断し、文言で結果を伝えるように変更しました。
resultメソッドでは、入力値・判断種目・判断基準(メソッド)を引数に、
受け取った判断基準の結果に応じた文言を返します。
ここでは、IsEvenという偶数判断のメソッドを宣言していますが、resultメソッドに渡したいのは
num % 2 == 0;
という条件を渡したいだけなので、delegateキーワードを使って直接メソッドを定義します。
staticvoidMain(string[]args){intnum=int.Parse(Console.ReadLine());Console.WriteLine(result(num,"偶数",delegate(intn){returnn%2==0;}));Console.ReadKey();}//入力値の判定結果(文言)を返すメソッドpublicstaticstringresult(intnum,stringcategory,Predicate<int>judge){if(judge(num)==true)return$"あなたが入力した数値:{num}は{category}です。";elsereturn$"あなたが入力した数値:{num}は{category}ではありません。";}
下記のところが、匿名メソッド(名前のないメソッド)になります。
delegate(intn){returnn%2==0;}
わざわざメソッドを定義する必要のないものについては匿名メソッドを使うことで
簡単に条件を渡せるようになったのですが、これを引数として渡すのはちょっと見ずらいです。。
そんな匿名メソッドのところをもっと簡単に書くことができるのが”ラムダ式”になってきます。
ラムダ式にする
匿名メソッドのところを、ラムダ式で書くと以下のようになります。
staticvoidMain(string[]args){intnum=int.Parse(Console.ReadLine());Console.WriteLine(result(num,"偶数",n=>n%2==0));Console.ReadKey();}publicdelegateboolJudgement(intvalue);//入力値の判定結果(文言)を返すメソッドpublicstaticstringresult(intnum,stringcategory,Predicate<int>judge){if(judge(num)==true)return$"あなたが入力した数値:{num}は{category}です。";elsereturn$"あなたが入力した数値:{num}は{category}ではありません。";}
result(num,"偶数",n=>n%2==0)
なんかシンプルになりました。。
なんでこんな形になるのかわからなかったのですが、冗長なラムダ式から
順に簡潔にしていくことでなるほど、そういうことだったのかと自分は理解することができました。
冗長なラムダ式から簡潔にしていく
1
result(num,"偶数",(intn)=>{if(n%2==0)returntrue;elsereturnfalse;});
delegateキーワードの代わりに =>(ラムダ演算子)が使われている。
=>の左側が引数の宣言で、右側がメソッドになる。
上記のラムダ式を順に簡潔にしていく。
2
result(num,"偶数",(intn)=>{returnn%2==0;});
・returnの横には式が書けること
・n % 2 == 0 の式で、成立すればbool型の値(True or False)が返る
上記の理由より、if文をなくすことができる
3
result(num,"偶数",(intn)=>n%2==0);
ラムダ式の{}の中が1つの文の場合は{}とreturnを省略可能
4
result(num,"偶数",(n)=>n%2==0);
ラムダ式では、引数の型を省略可能
5
result(num,"偶数",n=>n%2==0);
引数が1つの場合は()を省略可能
これがラムダ式・・・!
まとめ
・デリゲート型にはメソッドを入れることができる
・簡単なメソッドなら、わざわざメソッドを定義する必要はなく匿名メソッドで表現可能
・匿名メソッドを簡単に記述できるのがラムダ式
・ラムダ式を冗長な状態から順に簡潔にしていくことで構造を理解
所感
メソッドの引数にメソッドを入れられることを知りびっくりしました。
また、ラムダ式とかデリゲートとかネットを見るだけじゃピンとこなかったです。
実際に自分の手でやってみないと理解できないですね(-_-;)
あとは実践でどんどん使うことで理解を深めれたらと思います。
あと、このように実際に文章に起こすことは自分の理解を深めることができるのでいいですね!
この記事を作るのに大分時間はかかってしまいましたが。。
これからも学んだことを自分なりに落とし込んで、またoutputしていこうと思います。
参考・引用元
実戦で役立つ c#プログラミングのイディオム/定石&パターン
C#ラムダ式 基礎文法最速マスター
C# によるプログラミング入門
最速の素数判定プログラム C# Java C++