LoginSignup
6
6

More than 3 years have passed since last update.

C# ラムダ式を理解するための備忘録

Posted at

初めに

初投稿です。
C#初心者です。仕事で使うため現在勉強中です。
ラムダ式について、LINQとかで形式的に使えるけどなぜそう書くのかよくわかっていなかったので、
以下の書籍を参考に、自分の備忘録(Output)のため記事を投稿しました。
自分が学んだことを記事として投稿(文章化)することで、自分の知識にしようという試みです。

参考書籍:実戦で役立つ c#プログラミングのイディオム/定石&パターン

ラムダ式とは?

C#ラムダ式 基礎文法最速マスターより引用

C#言語のラムダ式(lambda expressions)とは、デリゲート(delegate)や、メソッド・ベースのLINQ文の(例えば)WhereメソッドやSelectメソッドなどの引数をシンプルに記述するために、C# 3.0(=Visual C# 2008)以降で導入された言語仕様である。


僕はよくわかりませんでした。。
そもそもデリゲート(delegate)って?

まずはデリゲートを理解する

C# によるプログラミング入門より引用

デリゲート(delegate: 代表、委譲、委託)とは、メソッドを参照するための型です。

自分はこう解釈しました。
delgate型:〇〇の型で受け取って、××の型で返すメソッドを代入できる型

例えば

delegate bool judgement(int value);

→int型で値を受け取って、bool型の値を返すメソッドを代入することができる
 つまるところ、入力と出力さえ合っていいれば中身がどんなメソッドでも代入することができる・・・?

試しに、Consoleから入力した数字について、何らかの判断をしてTrueかFalseを返すプログラムで確認

        static void Main(string[] args)
        {

            int num = int.Parse(Console.ReadLine());

            Judgement judge = メソッド名;

            Console.WriteLine(judge(num));
            Console.ReadKey();

        }

        public delegate bool Judgement(int value);

偶数判断のメソッドを追加して、メソッド名のところに”IsEven”を代入すると、

        static void Main(string[] args)
        {

            int num = int.Parse(Console.ReadLine());

            Judgement judge = IsEven;

            Console.WriteLine(judge(num));
            Console.ReadKey();

        }

        public delegate bool Judgement(int value);

        public static bool IsEven(int num)
        {
            return num % 2 == 0;
        }

入力した数値が偶数であればTrue,偶数でなければFalesを返します。
奇数判断メソッドを追加して”IsOdd”を代入すると、

        static void Main(string[] args)
        {

            int num = int.Parse(Console.ReadLine());

            Judgement judge = IsOdd;

            Console.WriteLine(judge(num));
            Console.ReadKey();

        }

        public delegate bool Judgement(int value);

        public static bool IsEven(int num)
        {
            return num % 2 == 0;
        }
        public static bool IsOdd(int num)
        {
            return num % 2 == 1;
        }

奇数かどうかをTrue、Falseで返してきます。
素数判断メソッドを追加しても


        static void Main(string[] args)
        {

            int num = int.Parse(Console.ReadLine());

            Judgement judge = IsPrime;

            Console.WriteLine(judge(num));
            Console.ReadKey();

        }

        public delegate bool Judgement(int value);

        public static bool IsPrime(int num)
        {
            if (num < 2) return false;
            else if (num == 2) return true;
            else if (num % 2 == 0) return false; // 偶数はあらかじめ除く

            double sqrtNum = Math.Sqrt(num);
            for (int i = 3; i <= sqrtNum; i += 2)
            {
                if (num % i == 0)
                {
                    // 素数ではない
                    return false;
                }
            }

            // 素数である
            return true;
        }


入力した数字が素数かどうかを判断することができるようになる。

引用記事:最速の素数判定プログラム C# Java C++

つまり

delegate bool Judgement(int value);

で宣言したとしたら、int型の引数でbool型の返り値のメソッドだったらなんでも入るってことですね。

でも、偶数判定とか奇数判定とか1文で終わるようなものもいちいちメソッドを定義してあげないといけません。。煩わ((
 →メソッドをわざわざ定義しなくても、メソッドを渡してあげることができれば!

メソッドを定義することなくメソッドを渡してあげる方法

匿名メソッドを利用

 public delegate bool Predicate<in T>(T obj);

→Predicateは<T>型を受け取ってbool型を返すメソッドを代入することができる
 ※Tのところはintでもstringでもなんでもござれ

Predicateを使うことで、わざわざdelegate型を自分で定義して宣言する必要がなくなる!
※ちなみに、Predicate以外にもこんなデリゲートがある
 ・Action<T> T型を受け取って返り値がない(void)のメソッドを代入可能
 ・Func<T> T型の返り値を返す、引数がないメソッドを代入可能

ちょっとプログラムを改修して確かめてみる

        static void Main(string[] args)
        {

            int num = int.Parse(Console.ReadLine());

            Console.WriteLine(result(num, "偶数", IsEven));
            Console.ReadKey();

        }


        //入力値の判定結果(文言)を返すメソッド
        public static string result(int num, string category,Predicate<int> judge)
        {
            if (judge(num) == true)
                return $"あなたが入力した数値:{num}{category}です。";
            else
                return $"あなたが入力した数値:{num}{category}ではありません。";

        }

        //偶数ならTrueを返すメソッド
        public static bool IsEven(int num)
        {
            return num % 2 == 0;
        }

入力した値について偶数かどうかを判断し、文言で結果を伝えるように変更しました。
resultメソッドでは、入力値・判断種目・判断基準(メソッド)を引数に、
受け取った判断基準の結果に応じた文言を返します。

ここでは、IsEvenという偶数判断のメソッドを宣言していますが、resultメソッドに渡したいのは
num % 2 == 0;
という条件を渡したいだけなので、delegateキーワードを使って直接メソッドを定義します。


        static void Main(string[] args)
        {

            int num = int.Parse(Console.ReadLine());

            Console.WriteLine(result(num, "偶数", delegate(int n) { return n % 2 == 0; }));
            Console.ReadKey();

        }


        //入力値の判定結果(文言)を返すメソッド
        public static string result(int num, string category,Predicate<int> judge)
        {
            if (judge(num) == true)
                return $"あなたが入力した数値:{num}{category}です。";
            else
                return $"あなたが入力した数値:{num}{category}ではありません。";

        }

下記のところが、匿名メソッド(名前のないメソッド)になります。


delegate(int n) { return n % 2 == 0; }

わざわざメソッドを定義する必要のないものについては匿名メソッドを使うことで
簡単に条件を渡せるようになったのですが、これを引数として渡すのはちょっと見ずらいです。。

そんな匿名メソッドのところをもっと簡単に書くことができるのが”ラムダ式”になってきます。

ラムダ式にする

匿名メソッドのところを、ラムダ式で書くと以下のようになります。


        static void Main(string[] args)
        {

            int num = int.Parse(Console.ReadLine());

            Console.WriteLine(result(num, "偶数", n => n % 2 == 0));
            Console.ReadKey();

        }

        public delegate bool Judgement(int value);

        //入力値の判定結果(文言)を返すメソッド
        public static string result(int num, string category,Predicate<int> judge)
        {
            if (judge(num) == true)
                return $"あなたが入力した数値:{num}{category}です。";
            else
                return $"あなたが入力した数値:{num}{category}ではありません。";

        }

result(num, "偶数", n => n % 2 == 0)

なんかシンプルになりました。。
なんでこんな形になるのかわからなかったのですが、冗長なラムダ式から
順に簡潔にしていくことでなるほど、そういうことだったのかと自分は理解することができました。

冗長なラムダ式から簡潔にしていく

1

            result(num,"偶数",
            (int n) =>
            {
               if( n % 2 == 0)
                return true;
               else
                return false;

            });

delegateキーワードの代わりに =>(ラムダ演算子)が使われている。
=>の左側が引数の宣言で、右側がメソッドになる。
上記のラムダ式を順に簡潔にしていく。

2

result(num,"偶数", (int n) => { return n % 2 == 0;});

・returnの横には式が書けること
・n % 2 == 0 の式で、成立すればbool型の値(True or False)が返る
上記の理由より、if文をなくすことができる

3

result(num,"偶数", (int n) => 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++

6
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
6