LINQ - Expresiones Lambda

El término 'expresión lambda' ha derivado su nombre del cálculo 'lambda' que a su vez es una notación matemática aplicada para definir funciones. Las expresiones lambda como parte ejecutable de una ecuación LINQ traducen la lógica de una manera en el tiempo de ejecución para que pueda pasar a la fuente de datos de manera conveniente. Sin embargo, las expresiones lambda no se limitan a encontrar aplicaciones solo en LINQ.

Estas expresiones se expresan mediante la siguiente sintaxis:

(Input parameters) ⇒ Expression or statement block

Aquí hay un ejemplo de una expresión lambda:

y ⇒ y * y

La expresión anterior especifica un parámetro llamado y y ese valor de y se eleva al cuadrado. Sin embargo, no es posible ejecutar una expresión lambda en esta forma. A continuación se muestra un ejemplo de una expresión lambda en C #.

C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lambdaexample {
   class Program {

      delegate int del(int i);
      static void Main(string[] args) {

         del myDelegate = y ⇒ y * y;
         int j = myDelegate(5);
         Console.WriteLine(j);
         Console.ReadLine();
      }
   }
}

VB

Module Module1
   Private Delegate Function del(ByVal i As Integer) As Integer
   
   Sub Main(ByVal args As String())
   
      Dim myDelegate As del = Function(y) y * y
      Dim j As Integer = myDelegate(5)
      Console.WriteLine(j)
      Console.ReadLine()
	  
   End Sub
   
End Module

Cuando el código anterior de C # o VB se compila y ejecuta, produce el siguiente resultado:

25

Expresión Lambda

Como la expresión en la sintaxis de la expresión lambda que se muestra arriba está en el lado derecho, también se conocen como expresión lambda.

Lambdas asíncronas

La expresión lambda creada mediante la incorporación de procesamiento asincrónico mediante el uso de la palabra clave async se conoce como lambdas asincrónicas. A continuación se muestra un ejemplo de lambda asíncrono.

Func<Task<string>> getWordAsync = async()⇒ “hello”;

Lambda en operadores de consulta estándar

Una expresión lambda dentro de un operador de consulta es evaluada por el mismo a pedido y trabaja continuamente en cada uno de los elementos de la secuencia de entrada y no en toda la secuencia. La expresión Lambda permite a los desarrolladores introducir su propia lógica en los operadores de consulta estándar. En el siguiente ejemplo, el desarrollador ha utilizado el operador 'Dónde' para recuperar los valores impares de la lista dada haciendo uso de una expresión lambda.

C#

//Get the average of the odd Fibonacci numbers in the series... 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lambdaexample {
   class Program {     
      static void Main(string[] args) {
      
         int[] fibNum = { 1, 1, 2, 3, 5, 8, 13, 21, 34 };
         double averageValue = fibNum.Where(num ⇒ num % 2 == 1).Average();
         Console.WriteLine(averageValue);
         Console.ReadLine();
      }
   }
}

VB

Module Module1

   Sub Main()
   
      Dim fibNum As Integer() = {1, 1, 2, 3, 5, 8, 13, 21, 34}
      Dim averageValue As Double = fibNum.Where(Function(num) num Mod 2 = 1).Average()
	  
      Console.WriteLine(averageValue)
      Console.ReadLine()
	  
   End Sub
   
End Module

Cuando el código anterior se compila y ejecuta, produce el siguiente resultado:

7.33333333333333

Inferencia de tipos en Lambda

En C #, la inferencia de tipos se usa convenientemente en una variedad de situaciones y también sin especificar los tipos explícitamente. Sin embargo, en el caso de una expresión lambda, la inferencia de tipos funcionará solo cuando se haya especificado cada tipo, ya que el compilador debe cumplirse. Consideremos el siguiente ejemplo.

delegate int Transformer (int i);

Aquí el compilador emplea la inferencia de tipos para basarse en el hecho de que x es un número entero y esto se hace examinando el tipo de parámetro del Transformer.

Alcance variable en expresión lambda

Existen algunas reglas al usar el alcance de la variable en una expresión lambda, como las variables que se inician dentro de una expresión lambda no están destinadas a ser visibles en un método externo. También existe una regla de que una variable capturada no debe ser recolectada como basura a menos que el delegado que hace referencia a la misma sea elegible para el acto de recolección de basura. Además, existe una regla que prohíbe una declaración de devolución dentro de una expresión lambda para provocar la devolución de un método adjunto.

A continuación, se muestra un ejemplo para demostrar el alcance de la variable en la expresión lambda.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace lambdaexample {
   class Program {
      delegate bool D();
      delegate bool D2(int i);

      class Test {
         D del;
         D2 del2;
			
         public void TestMethod(int input) {
            int j = 0;
            // Initialize the delegates with lambda expressions.
            // Note access to 2 outer variables.
            // del will be invoked within this method.
            del = () ⇒ { j = 10; return j > input; };

            // del2 will be invoked after TestMethod goes out of scope.
            del2 = (x) ⇒ { return x == j; };

            // Demonstrate value of j:            
            // The delegate has not been invoked yet.
            Console.WriteLine("j = {0}", j);        // Invoke the delegate.
            bool boolResult = del();
           
            Console.WriteLine("j = {0}. b = {1}", j, boolResult);
         }

         static void Main() {
            Test test = new Test();
            test.TestMethod(5);

            // Prove that del2 still has a copy of
            // local variable j from TestMethod.
            bool result = test.del2(10);
           
            Console.WriteLine(result);

            Console.ReadKey();
         }
      }
   }
}

Cuando el código anterior se compila y ejecuta, produce el siguiente resultado:

j = 0
j = 10. b = True
True

Árbol de expresión

Las expresiones lambda se utilizan en Expression Treeconstrucción extensamente. Un árbol de expresión regala código en una estructura de datos que se asemeja a un árbol en el que cada nodo es en sí mismo una expresión como una llamada a un método o puede ser una operación binaria como x <y. A continuación se muestra un ejemplo del uso de la expresión lambda para construir un árbol de expresión.

Declaración Lambda

También hay statement lambdasque consta de dos o tres declaraciones, pero no se utilizan en la construcción de árboles de expresión. Una declaración de retorno debe escribirse en una instrucción lambda.

Sintaxis de la instrucción lambda

(params)⇒ {statements}

Ejemplo de una instrucción lambda

using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;

namespace lambdaexample {
   class Program {
      static void Main(string[] args) {
         int[] source = new[] { 3, 8, 4, 6, 1, 7, 9, 2, 4, 8 };

         foreach (int i in source.Where(x ⇒ 
            {
               if (x <= 3)
                  return true;
               else if (x >= 7)
                  return true;
               return false;
            }
         ))
        Console.WriteLine(i);
        Console.ReadLine();
      }
   }
}

Cuando el código anterior se compila y ejecuta, produce el siguiente resultado:

3
8
1
7
9
2
8

Lambdas se emplean como argumentos en consultas LINQ basadas en métodos y nunca se les permite tener un lugar en el lado izquierdo de operadores como is o asal igual que los métodos anónimos. Aunque las expresiones Lambda son métodos anónimos muy parecidos, estos no están restringidos en absoluto para usarse solo como delegados.

Puntos para recordar al usar expresiones lambda

  • Una expresión lambda puede devolver un valor y puede tener parámetros.

  • Los parámetros se pueden definir de muchas formas con una expresión lambda.

  • Si hay una sola declaración en una expresión lambda, no hay necesidad de llaves, mientras que si hay varias declaraciones, las llaves y el valor de retorno son esenciales para escribir.

  • Con las expresiones lambda, es posible acceder a las variables presentes fuera del bloque de expresión lambda mediante una función conocida como cierre. El uso del cierre debe realizarse con precaución para evitar cualquier problema.

  • Es imposible ejecutar un código inseguro dentro de cualquier expresión lambda.

  • Las expresiones lambda no están diseñadas para usarse en el lado izquierdo del operador.