ラムダ式

更新 : 2007 年 11 月

ラムダ式とは、計算を実行して単一の値を返す、名前を持たない関数です。ラムダ式は、デリゲート型が有効な場所で使用できます。

Bb531253.alert_note(ja-jp,VS.90).gifメモ :

RemoveHandler ステートメントは例外です。ラムダ式は、RemoveHandler のデリゲート パラメータには渡せません。

次の例は、引数をインクリメントし、その値を返すラムダ式を示します。

Function (num As Integer) num + 1

ラムダ式は式であるため、ステートメントの一部としてのみ使用できます。

たとえば、特にこの関数を複数回使用する場合などには、変数名を割り当てて使用できます。

Dim add1 = Function(num As Integer) num + 1

関数を呼び出すには、パラメータの値を指定します。

' The following line prints 6.
Console.WriteLine(add1(5))

または、関数の宣言と実行を同時に行うことができます。

Console.WriteLine((Function(num As Integer) num + 1)(5))

ラムダ式は、関数呼び出しの戻り値として返すことができます (このトピックの「コンテキスト」セクションに例が示されています)。また、デリゲート パラメータの引数として渡すこともできます。次の例では、testResult メソッドの引数として Boolean 型のラムダ式を渡しています。このメソッドは、整数型の引数 value に対してブール値の評価を行い、ラムダ式が value を適用したときに True を返した場合は "Success" を表示し、False を返した場合は "Failure" を表示します。

Module Module2

    Sub Main()
        ' The following line will print Success, because 4 is even.
        testResult(4, Function(num) num Mod 2 = 0)
        ' The following line will print Failure, because 5 is not > 10.
        testResult(5, Function(num) num > 10)
    End Sub

    ' Sub testResult takes two arguments, an integer value and a 
    ' Boolean function. 
    ' If the function returns True for the integer argument, Success
    ' is displayed.
    ' If the function returns False for the integer argument, Failure
    ' is displayed.
    Sub testResult(ByVal value As Integer, ByVal fun As Func(Of Integer, Boolean))
        If fun(value) Then
            Console.WriteLine("Success")
        Else
            Console.WriteLine("Failure")
        End If
    End Sub

End Module

クエリでのラムダ式の使用

統合言語クエリ (LINQ: Language-Integrated Query) では、ラムダ式は、多くの標準クエリ演算子の基礎になっています。コンパイラは、ラムダ式を作成して、Where、Select、Order By、Take While などの基本的なクエリ メソッドに定義された計算を取り込みます。

たとえば、次のようなクエリがあるとします。

Dim londonCusts = From cust In db.Customers 
                  Where cust.City = "London" 
                  Select cust

この例は、次のコードにコンパイルされます。

Dim londonCusts = db.Customers _
    .Where(Function(cust) cust.City = "London") _
    .Select(Function(cust) cust)

クエリ メソッドの詳細については、「クエリ (Visual Basic)」を参照してください。

ラムダ式の構文

ラムダ式の構文は、標準関数の構文に似ています。相違点を次に示します。

  • ラムダ式には名前がありません。

  • ラムダ式では、Overloads や Overrides などの修飾子を使用できません。

  • ラムダ式では、As 句を使用して関数の戻り値の型を指定することはできません。代わりに、ラムダ式の本体を評価した値から型が推論されます。たとえば、ラムダ式の本体が Where cust.City = "London" の場合、その戻り値の型は Boolean になります。

  • 関数の本体は、ステートメントではなく式である必要があります。本体を関数プロシージャへの呼び出しで構成することはできますが、サブ プロシージャへの呼び出しは指定できません。

  • Return ステートメントはありません。関数によって返される値は、関数の本体に指定された式の値です。

  • End Function ステートメントはありません。

  • すべてのパラメータは、データ型が指定されているか推論される必要があります。

  • Optional パラメータと ParamArray パラメータは使用できません。

  • ジェネリック パラメータは使用できません。

これらの制限とラムダ式の使用方法から、通常、ラムダ式は短くて単純です。

コンテキスト

ラムダ式は、それが定義されている外側のメソッドとコンテキストを共有します。ラムダ式は、コンテナ メソッドに記述されているすべてのコードと同じアクセス権を持ちます。これには、コンテナ メソッド内のメンバ変数、関数、サブ関数、Me、パラメータ、およびローカル変数へのアクセスも含まれます。

コンテナ メソッド内のローカル変数とパラメータは、そのメソッドの有効期間以降もアクセスされる可能性があります。ラムダ式を参照するデリゲートがガベージ コレクションの対象にならない限り、元の環境での変数へのアクセスは保持されます。次の例では、変数 target は、ラムダ式 playTheGame が定義されているメソッド makeTheGame にローカルです。返されたラムダ式は、Main で takeAGuess に割り当てられ、ローカル変数 target へのアクセスを保持し続けていることに注意してください。

Module Module1

    Sub Main()
        ' Variable takeAGuess is a Boolean function. It stores the target
        ' number that is set in makeTheGame.
        Dim takeAGuess As gameDelegate = makeTheGame()

        ' Set up the loop to play the game.
        Dim guess As Integer
        Dim gameOver = False
        While Not gameOver
            guess = CInt(InputBox("Enter a number between 1 and 10 (0 to quit)", "Guessing Game", "0"))
            ' A guess of 0 means you want to give up.
            If guess = 0 Then
                gameOver = True
            Else
                ' Tests your guess and announces whether you are correct. Method takeAGuess
                ' is called multiple times with different guesses. The target value is not 
                ' accessible from Main and is not passed in.
                gameOver = takeAGuess(guess)
                Console.WriteLine("Guess of " & guess & " is " & gameOver)
            End If
        End While

    End Sub

    Delegate Function gameDelegate(ByVal aGuess As Integer) As Boolean

    Public Function makeTheGame() As gameDelegate

        ' Generate the target number, between 1 and 10. Notice that 
        ' target is a local variable. After you return from makeTheGame,
        ' it is not directly accessible.
        Randomize()
        Dim target As Integer = CInt(Int(10 * Rnd() + 1))

        ' Print the answer if you want to be sure the game is not cheating
        ' by changing the target at each guess.
        Console.WriteLine("(Peeking at the answer) The target is " & target)

        ' The game is returned as a lambda expression. The lambda expression
        ' carries with it the environment in which it was created. This 
        ' environment includes the target number. Note that only the current
        ' guess is a parameter to the returned lambda expression, not the target. 

        ' Does the guess equal the target?
        Dim playTheGame = Function(guess As Integer) guess = target

        Return playTheGame

    End Function

End Module

次の例では、入れ子になったラムダ式の広範囲のアクセス権を示します。返されたラムダ式は、Main から aDel として実行されるときに、これらの要素にアクセスします。

  • ラムダ式が定義されるクラスのフィールド : aField

  • ラムダ式が定義されるクラスのプロパティ : aProp

  • ラムダ式が定義される functionWithNestedLambda メソッドのパラメータ : level1

  • functionWithNestedLambda のローカル変数 : localVar

  • ラムダ式がネストされるラムダ式のパラメータ : level2

Module Module3

    Sub Main()
        ' Create an instance of the class, with 1 as the value of 
        ' the property.
        Dim lambdaScopeDemoInstance = New LambdaScopeDemoClass _
            With {.Prop = 1}

        ' Variable aDel will be bound to the nested lambda expression  
        ' returned by the call to functionWithNestedLambda.
        ' The value 2 is sent in for parameter level1.
        Dim aDel As aDelegate = _
            lambdaScopeDemoInstance.functionWithNestedLambda(2)

        ' Now the returned lambda expression is called, with 4 as the 
        ' value of parameter level3.
        Console.WriteLine("First value returned by aDel:   " & aDel(4))

        ' Change a few values to verify that the lambda expression has 
        ' access to the variables, not just their original values.
        lambdaScopeDemoInstance.aField = 20
        lambdaScopeDemoInstance.Prop = 30
        Console.WriteLine("Second value returned by aDel: " & aDel(40))
    End Sub

    Delegate Function aDelegate(ByVal delParameter As Integer) _
        As Integer

    Public Class LambdaScopeDemoClass
        Public aField As Integer = 6
        Dim aProp As Integer

        Property Prop() As Integer
            Get
                Return aProp
            End Get
            Set(ByVal value As Integer)
                aProp = value
            End Set
        End Property

        Public Function functionWithNestedLambda _
           (ByVal level1 As Integer) As aDelegate
            Dim localVar As Integer = 5

            ' When the nested lambda expression is executed the first 
            ' time, as aDel from Main, the variables have these values:
            ' level1 = 2
            ' level2 = 3, after aLambda is called in the Return statement
            ' level3 = 4, after aDel is called in Main
            ' locarVar = 5
            ' aField = 6
            ' aProp = 1
            ' The second time it is executed, two values have changed:
            ' aField = 20
            ' aProp = 30
            ' level3 = 40
            Dim aLambda = Function(level2 As Integer) _
                              Function(level3 As Integer) _
                                  level1 + level2 + level3 + localVar _
                                  + aField + aProp

            ' The function returns the nested lambda, with 3 as the 
            ' value of parameter level2.
            Return aLambda(3)
        End Function

    End Class
End Module

デリゲート型への変換

ラムダ式は、互換性のあるデリゲート式に明示的に変換できます。互換性の一般的な要件の詳細については、「厳密でないデリゲート変換」を参照してください。

さらに、ラムダ式をデリゲートに割り当てるときは、パラメータ名を指定してデータ型を省略することで、デリゲートから型が取得されるようにできます。次の例では、ExampleDel 型は、整数型と文字列型の 2 つのパラメータを受け取るデリゲートです。この型の del という名前の変数に、ラムダ式が割り当てられています。ラムダ式には、パラメータのデータ型が指定されていないことに注意してください。ただし、ExampleDel の定義で指定されているように、del には整数型と文字列型の引数が必要です。

' Definition of function delegate ExampleDel.
Delegate Function ExampleDel(ByVal arg1 As Integer, _
                             ByVal arg2 As String) As Integer
' Declaration of del as an instance of ExampleDel, with no data 
' type specified for the parameters, m and s.
Dim del As ExampleDel = Function(m, s) m

' Valid call to del, sending in an integer and a string.
Console.WriteLine(del(7, "up"))

' Neither of these calls is valid. Function del requires an integer
' argument and a string argument.
' Not valid.
' Console.WriteLine(del(7, 3))
' Console.WriteLine(del("abc"))

  • 次の例は、null 許容引数に値が割り当てられた場合は True を返し、値が Nothing の場合は False を返すラムダ式を定義します。

    Dim notNothing = Function(num? As Integer) _
                 num IsNot Nothing
    Dim arg As Integer = 14
    Console.WriteLine("Does the argument have an assigned value?")
    Console.WriteLine(notNothing(arg))
    
  • 次の例は、配列内の最後の要素のインデックスを返すラムダ式を定義します。

    Dim numbers() As Integer = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
    Dim lastIndex = Function(intArray() As Integer) _
                        intArray.Length - 1
    For i = 0 To lastIndex(numbers)
        numbers(i) = numbers(i) + 1
    Next
    

参照

処理手順

方法 : Visual Basic でプロシージャを別のプロシージャに渡す

方法 : ラムダ式を作成する

概念

Visual Basic におけるプロシージャ

Visual Basic における LINQ の概要

デリゲートと AddressOf 演算子

null 許容値型

厳密でないデリゲート変換

参照

Function ステートメント (Visual Basic)