連載:C# 3.0入門

第1回 ラムダ式

株式会社ピーデー 川俣 晶
2008/04/04

ラムダ式の使用例

 ここまでのサンプル・コードの大多数は、あくまで構文や機能を説明するもので、必ずしもラムダ式を使用しなくても書けるものが多かった。最後に、ある程度実用性のあるラムダ式の使用例をお見せしよう。

 ここでは、整数の配列の各要素を入力とし、その値で割り算を実行して結果を出力するプログラムを作成するとしよう。ここで割り算を使ったのは、入力として受け付けられない値(0)が存在するからである(つまり、このサンプル・コードは不正な入力値が存在する事例一般に応用できる可能性がある)。

 さて、このプログラムは入力が0の場合は、その旨を出力して終了しなければならない。この意図をラムダ式抜きで記述するとしたら、どのように書くだろうか?

 この問い掛けのポイントは2つある。

  • 計算を行うループ内で値の0チェックを行うと、計算が始まった後で中断が発生する。つまり、計算値とエラー・メッセージの双方が出力されてしまう可能性がある
  • 計算値を出さずにエラー・メッセージを出すには、計算ループよりも手前に0チェックのためのループを設ける必要がある。しかし、それを書くとループが2つになって冗長すぎる

 つまり、どちらの方法もデメリットを抱えている。筆者であれば、「計算開始前にチェックを完了すべき」という要求が存在しなければ、冗長さを除去する方が重要だと考え、ループ内での判定の方を選ぶ。

using System;

class Program
{
  static void Main(string[] args)
  {
    int[] a = { 1, 2, 0 };

    foreach (int n in a)
    {
      if ( n == 0 )
      {
        Console.WriteLine("テストデータに0が含まれています。");
        return;
      }
      Console.WriteLine(100 / n);
    }
    // 出力:
    // 100
    // 50
    // テストデータに0が含まれています。
  }
}
リスト18 ループ内で判定する場合

 しかし、ラムダ式を使用してよければ、まったく別個の答えが出せる。配列の要素がすべて特定の条件を満たしているかを調べるだけなら、メソッド呼び出し1つとシンプルなラムダ式1つでケリがつくからだ。これは、読みやすさを損なわない範囲内で、if文の条件として楽に記述できる文字数となる。

 実際に記述した例を以下に示す。Anyメソッドは、コレクションの要素の中に1つでも引数のラムダ式を満たすものが含まれていればtrueになる。これは.NET Framework 3.5で拡張されたメソッドであり、使用するためにはコードの先頭に「using System.Linq;」が必要である。

using System;
using System.Linq; // Anyメソッドを使うために必要

class Program
{
  static void Main(string[] args)
  {
    int[] a = { 1, 2, 0 };

    if (a.Any(x => x == 0))
    {
      Console.WriteLine("テストデータに0が含まれています。");
      return;
    }
    foreach (int n in a) Console.WriteLine(100 / n);
  }
  // 出力:テストデータに0が含まれています。
}
リスト19 ラムダ式を用いて計算開始前に0チェックを行う

 もし.NET Framework 3.5が使用できない場合は、リスト19のif文を以下のように記述することで(やや冗長になるが).NET Framework 2.0でも同様のチェックを実現できる。

if (Array.FindIndex(a, x => x == 0) >= 0)

 この例は、データの固まり(コレクション)に対する処理の指示を短いラムダ式で記述することにより、1つの値に対するチェックである「if ( n == 0 )」と同じような感覚でコレクションに対するチェック「if (a.Any(x => x == 0))」を記述できることを示している。

 つまり、これまでコレクションに対するループとして記述されてきたコレクション要素の判定処理が、たった1つのif文に置換できることを示している。このようなテクニックを常用すると、明らかにソース・コードの雰囲気が変わり、簡潔になる。

【コラム】“=>”は不等号?

 BASIC系の言語などでは、不等号を記述する際に「=」と「>」の順番が規定されていないことがある。そのような言語では、「>=」と「=>」の意味は等価である。そのため、このような言語に親しんでいると「=>演算子」が不等号を記述しているように見えることがあるかもしれない。

 例えば以下のようなラムダ式は実際にあり得るし、これは上記のような言語の経験者にはまさに不等号そのもの見えるだろう。

x => 0

 ただし、Visual BasicのIDEを使うプログラマーであれば、不等号としての「=>」はめったに見ていないはずである。なぜかといえば、IDEで入力した際に、「=>」は即座に「>=」に置き換えられてしまうからである(Visual Basic 6.0とVisual Studio 2008で確認)。

 それらを考え合わせると、「=>」を不等号として扱うことのできるプログラミング言語でコードを書く場合でも、そのような表記は使わないように習慣づけるとよいだろう。

ラムダ式のさまざまなバリエーション

 ラムダ式には、さまざまな書き方のバリエーションがある。特に引数が1つの場合のみ、引数を囲うカッコを省略できるといった特殊な構文も存在するので、全ぼうを把握しにくい。そこで、主立った書き方のバリエーションを以下にまとめておく。

using System;

delegate int 引数なし();
delegate int 引数1つ(int onlyOne);
delegate int 引数2つ(int first, int second);
delegate void 戻り値なし();

class Program
{
  static void Main(string[] args)
  {
    引数なし sample1 = () => 0;
    引数1つ sample2 = (x) => x * 2;
    引数1つ sample3 = (int x) => x * 2;
    引数1つ sample4 = x => x * 2;
    引数2つ sample5 = (x, y) => x * y;
    引数2つ sample6 = (int x, int y) => x * y;
    引数1つ sample7 = (x) => { return x * 2; };
    引数1つ sample8 = (int x) => { return x * 2; };
    戻り値なし sample9 = () => Console.WriteLine("Hello!");
  }
}
リスト20 ラムダ式のさまざまなバリエーション

sample1 = () => 0

 引数が存在しないラムダ式を記述する場合は、引数のない空のカッコで書き始める。

sample2 = (x) => x * 2

 引数が存在する場合はそれを(x)のようにカッコで囲うのが基本である。

sample3 = (int x) => x * 2

 もちろん、引数に型指定を補ってもよい。補わねばならないケースは本文で紹介したとおり存在する。

sample4 = x => x * 2

 引数が1つの場合に限っては、引数を囲うカッコを省略してもよい。

sample5 = (x, y) => x * y

 引数が複数ある場合はカンマで区切って列挙する。

sample6 = (int x, int y) => x * y

 引数が複数ある場合でも、型指定は可能。

sample7 = (x) => { return x * 2; }

 ステートメント型のラムダとして記述した例。

sample8 = (int x) => { return x * 2; }

 ステートメント型のラムダでも引数の型は指定できる。

sample9 = () => Console.WriteLine("Hello!")

 戻り値がない場合は、式としてvoid型を返すメソッドの呼び出しを記述できる。

次回予告

 ここまでの説明で、ラムダ式のかなりの部分は説明できたと思う。今回の知識だけで、ソース・コードの質を大きく変えていくことができるだろう。

 しかし、これでラムダ式を完全に理解したと思うのは早い。

 実は、省略された型を調べる「型推論」という厄介な問題が残っているためだ。次回はこの型推論について説明したい。それに加えて、ラムダ式で使用するデリゲート型をクラス・ライブラリから利用する方法についても解説する予定である。例えば、今回のサンプル・コードでも使用したAction型やFunc型などである。End of Article


 INDEX
  C# 3.0入門
  第1回 ラムダ式
    1.C# 3.0とは何か?/C# 3.0の適用範囲
    2.ラムダ式は何をもたらすか
    3.ラムダ式と匿名メソッドの違い/ステートメント型のラムダ
    4.式形式のラムダの可能性/型指定の省略/何もしないラムダ式
  5.ラムダ式の使用例/ラムダ式のさまざまなバリエーション
 
インデックス・ページヘ  「C# 3.0入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間