SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

式ツリーからラムダ式を構築する

関数型構築を利用したラムダ式の動的な構築

  • X ポスト
  • このエントリーをはてなブックマークに追加

ラムダ式の動的な構築

 ラムダ式を記述するには、Functionキーワードを使用し、入力パラメータを指定し(各パラメータのデータ型も指定できます)、値を返すメソッド本体を記述します。ラムダ式の構成要素を求めるには、式をExpression(Of Delegate)型のインスタンスに割り当てます。ラムダ式を動的に構築したい(さまざまな要素をばらばらに求めたい)場合は、関数型構築を使用して、式の各要素を表すExpressionオブジェクトを定義します。動的なラムダ式を実行するには、Expression.Compileメソッドを呼び出します。コンパイルによって式ツリーのインスタンスがデリゲートとして返され、そのデリゲートを呼び出すことができます。

 System.Linq.Expressions.Expressionクラスは、すべての式要素の基本クラスです。さまざまな式要素(例えば定数など)を構築する各種の関数メソッドは、Expressionクラスの共有メソッドです。リスト1では、Function(i) i < 33というラムダ式を動的に構築し、この式ツリーの状態をLINQクエリでダンプし、式をコンパイルして呼び出しています。

リスト1 動的に構築され実行されるラムダ式
Imports System.Collections.Generic
Imports System.Linq
Imports System.Linq.Expressions
Imports System.Reflection


Module Module1

    Sub Main()

        ' doesn't do name look up on i so we have to use the same parameter 
        ' expression to associate the input parameter with the parameter in 
        ' the lambda body
        Dim param As ParameterExpression = Expression.Parameter(GetType(Integer), "i")

        Dim exp As LambdaExpression =
            Expression.Lambda(Of Func(Of Integer, Boolean))(
            Expression.LessThanOrEqual(
            param,
            Expression.Constant(33)),
            param
            )

        Dim mask As String = "{0} : {1}"
        Dim results = From prop In exp.GetType().GetProperties()
                      Select String.Format(mask, prop.Name, prop.GetValue(exp, Nothing))

        For Each item In results
            Console.WriteLine(item)
        Next

        Dim lambda As Func(Of Integer, Boolean) = CType(exp.Compile(), Func(Of Integer, Boolean))

        Console.WriteLine("{0}: {1}", exp.Body, lambda(10))
        Console.ReadLine()

    End Sub

End Module

 このコード例は、Microsoft Visual Studio 2010で作成しています。この例を見てまず気付くのは、VB2010では行継続文字がもう必要ないということです(これは見逃せませんね)。もう1つは、ParameterExpressionが単独で定義されているという点です。ParameterExpressionは、入力引数iと、この引数の関数本体での使用位置を表します。これは、ラムダ式の引数を使用する際に知っておく必要のある特別な「トリック」です(ただし、ヘルプファイルにはこのことについてほとんど記述がありません)。実は、パラメータ名は情報提供のためだけに存在しています。ラムダ式の実行時には、名前によるルックアップは行われません。しかし、メソッド本体のコンストラクタとパラメータ部で同じ引数paramを使用することで、式コンパイラによってパラメータと、そのパラメータのメソッド本体での使用位置が結び付けられます。もしも、式ツリーを定義するときに、次のようにメソッド本体と入力パラメータに別々の呼び出しを使用した場合は、

Dim exp As LambdaExpression =
        Expression.Lambda(Of Func(Of Integer, Boolean))(
        Expression.LessThanOrEqual(
        Expression.Parameter(GetType(Integer), "i"),
        Expression.Constant(33)),
        Expression.Parameter(GetType(Integer), "i")
        )

 コンパイルされた式を実行しようとすると、InvalidOperationExceptionが発生します(図1を参照)。iパラメータが存在することを式が示していても、この式はエラーになります。なぜなら、パラメータをルックアップするときには、パラメータ名ではなくパラメータの推定が使われるからです。

図1 すべてのパラメータと、メソッド本体でのパラメータの使用位置で、1つのParameterExpressionインスタンスを使用する必要がある
図1 すべてのパラメータと、メソッド本体でのパラメータの使用位置で、1つのParameterExpressionインスタンスを使用する必要がある

 この例のLambdaExpressionオブジェクトは4つの部分から成ります。二項式LessThanOrEqualと、この式に含まれるParameterExpression式およびConstant式、さらにExpression.Lambda関数の最後の引数である入力パラメータです。このLambdaExpressionオブジェクトの生成後は、この式ツリーが基本的にラムダ式Function(i) i < 33を格納することになります。

 Dim resultsで始まるステートメントでは、LINQクエリを使用してオブジェクトの状態ダンパーを定義しています。この状態ダンパーとFor Eachループによって、LambdaExpressionオブジェクトの構成要素がコンソールに送られます(図2を参照)。

図2 LambdaExpressionオブジェクトの状態(結果が最終行に表示されている)
図2 LambdaExpressionオブジェクトの状態(結果が最終行に表示されている)

 Dim lambda as Func(of Integer, Boolean)ステートメントでは、LambdaExpession.Compile(コードではexp.Compile)の戻り値を受け取ります。簡単に言えば、ラムダ式ツリーはコンパイルされた後、ローカルのデリゲート変数に代入されて、呼び出し待ちの状態になります。最後の2行で動的なラムダ式を呼び出し、結果を表示し、ユーザーの[Enter]キー入力を待ちます。

まとめ

 この記事を書くまでは、私はパラメータ式の文字列の名前が情報提供のためだけに存在しているとは知りませんでした。Anders Hejjlsbergからのブログの返事を読んで、その理由に納得しました(もっとも、ヘルプファイルではこの件についてほとんど触れられていませんが)。ここで、Andersの返事の一部を紹介します。「式内で使用されるパラメータは、オブジェクトIDを通じて参照されます。名前の比較によって参照されるわけではありません。実際、式ツリーの観点からすれば、パラメータ名は単なる情報です。このような設計になっている理由は、型がSystem.Typeを通じて参照されるのと同様です。ここでも名前は使用されません。式ツリーは完全にバインドされており、名前のルックアップルールの実装とは関係がないのです(これは言語によっては異なるかもしれません)。」というわけで、本番環境用のラムダ生成プログラムを自作することがないとしても、物事の仕組みとその理由をより深く知ることで、予期しないニーズにもプロフェッショナルとして対応できるようになるでしょう。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Paul Kimmel(Paul Kimmel)

CodeGuruのVB Todayのコラムニスト。オブジェクト指向プログラミングや.NETについてさまざまな書籍を執筆。『Professional DevExpress ASP.NET Controls』(Wiley刊)はAmazon.comおよび大型書店で好評発売中。『Teach Yourself the...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/5026 2010/04/08 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング