CodeZine(コードジン)

特集ページ一覧

ラムダ式でステップアップ! C#のプログラムから汎用的なアルゴリズムを切り出すことで、LINQについての理解を深めよう

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2014/10/14 14:00

目次

9. ラムダ式を使った汎用的なアルゴリズムの適用

 前節では、匿名メソッドを使うことにより、Mainメソッド内の環境を汎用アルゴリズムであるFilterメソッドとForEachメソッドに渡すことができました。

ラムダ式

 C# 3.0で採用されたラムダ式を使えば、さらに簡潔に書くことができます。

 C#のラムダ式については、次のページを参照してください。

書籍のリストの中から、タイトルか出版社名に "リ" を含むものをコンソールに表示(ラムダ式版)

 では、先のプログラムをラムダ式を使って書いてみましょう。FilterとForEachの引数に渡しているbook => ... の部分が、それぞれラムダ式です。匿名メソッドと似た記述ですが、よりシンプルです。

Program.cs
using MyCollection;
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        var publisherList = new List<Publisher> {
            new Publisher { Name = "技術評論社"          },
            new Publisher { Name = "翔泳社"              },
            new Publisher { Name = "オライリー・ジャパン"},
            new Publisher { Name = "SBクリエイティブ"    }
        };

        var bookList      = new List<Book> {
            new Book { Code           = "4774149489"                        ,
                       Title          = "C# ポケットリファレンス"           ,
                       PublisherIndex = 0                                   },
            new Book { Code           = "4798114618"                        ,
                       Title          = "C#の絵本"                          ,
                       PublisherIndex = 1                                   },
            new Book { Code           = "4798122203"                        ,
                       Title          = "独習C# 第3版"                      ,
                       PublisherIndex = 1                                   },
            new Book { Code           = "4873116503"                        ,
                       Title          = "プログラミングC# 第7版"            ,
                       PublisherIndex = 2                                   },
            new Book { Code           = "4797361344"                        ,
                       Title          = "猫でもわかるC#プログラミング 第2版",
                       PublisherIndex = 3                                   }
        };

        var searchWord    = "リ";

        // searchWord や publisherList がスコープ内なので問題なく使える
        bookList.Filter (book => book.Title.Contains(searchWord) ||      // ラムダ式
                                 publisherList[book.PublisherIndex].Name
                                           .Contains(searchWord))
                .ForEach(book => Console.WriteLine(                      // ラムダ式
                                     "コード: {0}, タイトル: {1}, 出版社: {2}",
                                     book.Code, book.Title,
                                     publisherList[book.PublisherIndex].Name
                                 ));
    }
}

 実行結果は、元々のプログラムと同じです。

実行結果
コード: 4774149489, タイトル: C# ポケットリファレンス, 出版社: 技術評論社
コード: 4873116503, タイトル: プログラミングC# 第7版, 出版社: オライリー・ジャパン
コード: 4797361344, タイトル: 猫でもわかるC#プログラミング 第2版, 出版社: SBクリエイティブ

 ラムダ式を使うことで、余計な記述(本来のプログラムのロジックでない部分)が減り、簡潔な記述となりました。

Ildasmを使って仕組みを調べる

 では、匿名メソッドのときと同様、今度もビルドして作られたアセンブリを Ildasm.exe で開いて中の仕組みを見てみましょう。

Ildasmで開いたアセンブリ
Ildasmで開いたアセンブリ

 匿名メソッドのときとまったく同じ構造になっています。

 そして、匿名メソッドのときと同じように、ラムダ式にあたる「<Main>b__2 : book(class Book)」をダブルクリックして開いてみると、こちらもやはり同じです。

1つ目のラムダ式のIL(Intermediate Language:中間言語)
.method assembly hidebysig instance bool 
        '<Main>b__2'(class Book book) cil managed
{
  // コード サイズ       55 (0x37)
  .maxstack  8
  IL_0000:  ldarg.1
  IL_0001:  callvirt   instance string Book::get_Title()
  IL_0006:  ldarg.0
  IL_0007:  ldfld      string Program/'<>c__DisplayClass0'::searchWord
  IL_000c:  callvirt   instance bool [mscorlib]System.String::Contains(string)
  IL_0011:  brtrue.s   IL_0035
  IL_0013:  ldarg.0
  IL_0014:  ldfld      class [mscorlib]System.Collections.Generic.List`1<class Publisher> Program/'<>c__DisplayClass0'::publisherList
  IL_0019: ldarg.1
  IL_001a: callvirt instance int32 Book::get_PublisherIndex()
  IL_001f: callvirt instance !0 class [mscorlib]System.Collections.Generic.List`1<class Publisher>::get_Item(int32)
  IL_0024: callvirt instance string Publisher::get_Name()
  IL_0029: ldarg.0
  IL_002a: ldfld string Program/'<>c__DisplayClass0'::searchWord
  IL_002f: callvirt instance bool [mscorlib]System.String::Contains(string)
  IL_0034: ret
  IL_0035: ldc.i4.1
  IL_0036: ret
} // end of method '<>c__DisplayClass0'::'<Main>b__2'

 つまり、ラムダ式の場合も、匿名メソッドと同じ仕組みでクロージャーを実現していることが分かります。

ラムダ式と匿名メソッドのどちらを使えば良いのか?

 匿名メソッドとラムダ式は、どちらもクロージャーとして使うことができます。では、どちらを使えば良いのでしょうか?

 ラムダ式は、匿名メソッドより高機能であり(匿名メソッドで行えることがすべて行えるのに加え、式木としても使うことが可能)、ほとんどの場合、より簡潔に書けます。そのため、ラムダ式が使えるC# 3.0以降では匿名メソッドを使わずラムダ式の方を使うことをお勧めします。

10. まとめ

 C#における汎用的なアルゴリズムの分離について考察しました。

 汎用的なアルゴリズムがどのように分離されると良いか、そして分離されたアルゴリズムがどのように使われると良いか、を述べました。

 今回は汎用的なアルゴリズムを自作しましたが、このようなアルゴリズムはC#のライブラリの中にすでにたくさん存在し、同様に使うことができます。LINQがその代表で、今回作成したFilterのような便利で汎用的なメソッドを多数利用することができます。

 ラムダ式やLINQの便利さは、次のページでも紹介されています。

 ラムダ式を活用すれば、簡潔な記述で既存のアルゴリズムを使うことができます。

 C#で汎用的なアルゴリズムを利用したり書いたりするために、ここで述べたことが参考になれば幸いです。

  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 小島 富治雄(フジヲ)

    Microsoft MVP for C# (2005.07~)。 - 注目の MVP - Blog: プログラミング C# - 翔ソフトウェア (Sho's) - Web Site: 翔ソフトウェア (Sho's) - Twitter: Fujiwo...

あなたにオススメ

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5