Convirtiendo arboles de expresión lambda en diferentes strings.



Documentos relacionados
Generador de Proxy remoto JavaScript.

GESTIÓN DE EXCEPCIONES EN JAVA. CAPTURA CON BLOQUES TRY CATCH Y FINALLY. EJEMPLOS RESUELTOS. (CU00927C)

CONSULTAS CON SQL. 3. Hacer clic sobre el botón Nuevo de la ventana de la base de datos. Aparecerá el siguiente cuadro de diálogo.

Preliminares. Tipos de variables y Expresiones

Patrones para persistencia (I) Ingeniería del Software II

A estas alturas de nuestros conocimientos vamos a establecer dos reglas muy prácticas de cómo sumar dos números reales:

CONSULTAS DE RESUMEN SQL SERVER Manual de Referencia para usuarios. Salomón Ccance CCANCE WEBSITE

Fórmulas. Objetivos y Definición. Definir fórmulas nos brinda una forma clave de compartir conocimiento y obtener código generado optimizado

8. Sentencia return y métodos

Pruebas de unidad con JUnit

Programación en Java. Programación en OO

Guía de implementación Softland en SQL Server Versión 1.0

Modificación y parametrización del modulo de Solicitudes (Request) en el ERP/CRM Compiere.

Ecuaciones de primer grado con dos incógnitas

Cuestionario: Programación en C y máscaras (II)

Figura 4.1 Clasificación de los lenguajes de bases de datos

Ejercicios - Persistencia en Android: ficheros y SQLite

Curso de PHP con MySQL Gratis

Materia: Informática. Nota de Clases Sistemas de Numeración

Tema 2 : NÚMEROS ENTEROS. Primero de Educación Secundaria Obligatoria. I.e.s Fuentesaúco.

Modulo 1 El lenguaje Java

SISTEMAS DE NUMERACIÓN. Sistema decimal

Una variable de clase escalar tiene un nivel de indirección igual a 1. Por ejemplo, las variables i, b y x definidas como se muestra a continuación.

Base de datos Procedimientos Almacenados y Funciones

1. DML. Las subconsultas

CAPÍTULO 3 Servidor de Modelo de Usuario

Funcionalidades Software PROYECTOS GotelGest.Net Software para la gestión de Proyectos GotelGest.Net

Ciclo de vida y Metodologías para el desarrollo de SW Definición de la metodología

Bienvenidos a la presentación, producción de informes y depuración (debugging). En esta unidad discutiremos la producción de informes utilizando la

Las propiedades de la clase en java es el equivalente a las variables globales en lenguajes estructurados como el C.

Sintaxis y Convenciones de Java. M. en C. Erika Vilches

Aplicaciones seguras con ClaseSeguridad

LiLa Portal Guía para profesores

Manual del Usuario. Sistema de Help Desk

RESUMEN DE CONCEPTOS BASICOS DE PROGRAMACION JAVA

UNIDAD 1. LOS NÚMEROS ENTEROS.

Capitulo 6. Como echarle el muerto a alguien.

Manual de Usuario Módulo de Registro de Vehículos

FORMACIÓN DE EQUIPOS DE E-LEARNING 2.0 MÓDULO DE DISEÑO Y PRODUCCIÓN DE MATERIALES UNIDAD 6 B

Introducción a PHP. * No es necesario declarar previamente las variables.

Unidad I. 1.1 Sistemas numéricos (Binario, Octal, Decimal, Hexadecimal)

Constructores y Destructores

Modelo de Objetos Distribuidos

Carrito de Compras. Esta opción dentro de Jazz la podremos utilizar como cualquier otro carrito de compras de una página de Internet.

Proyecto MONO. Juantomás García. 1. Introducción. GNOME Hispano

TABLA DE DECISION. Consideremos la siguiente tabla, expresada en forma genérica, como ejemplo y establezcamos la manera en que debe leerse.

La Lección de Hoy es Distancia entre dos puntos. El cuál es la expectativa para el aprendizaje del estudiante CGT.5.G.1

COMANDOS DE SQL, OPERADORES, CLAUSULAS Y CONSULTAS SIMPLES DE SELECCIÓN

Tema 6. Reutilización de código. Programación Programación - Tema 6: Reutilización de código

Objetivos de la práctica: - Practicar uso de ficheros: abrir, cerrar y tratamiento de información contenida en el fichero.

Oracle vs Oracle por Rodolfo Yglesias Setiembre 2008

PL/SQL. Con PL/SQL vamos a poder programar las unidades de programa de la base de datos Oracle:

Procesadores de lenguaje Tema 5 Comprobación de tipos

Java Inicial (20 horas)

COMPARACIÓN DE ÁREAS DE FIGURAS POR ESTUDIANTES DE PRIMERO DE MAGISTERIO

Actualización de versión a Bizagi 10.x

Árboles AVL. Laboratorio de Programación II

SQL (Structured Query Language)

Enlaces relacionados:

ALGORITMICA Y PROGRAMACION POR OBJETOS I

Clases de apoyo de matemáticas Fracciones y decimales Escuela 765 Lago Puelo Provincia de Chubut

contabilidad

Programa para el Mejoramiento de la Enseñanza de la Matemática en ANEP Proyecto: Análisis, Reflexión y Producción. Fracciones

GUIA PROGRAMACIÓN ORIENTADA A OBJETOS

"Diseño, construcción e implementación de modelos matemáticos para el control automatizado de inventarios

Aspectos Básicos de Networking

AGREGAR UN EQUIPO A UNA RED Y COMPARTIR ARCHIVOS CON WINDOWS 7

LAS SUBCONSULTAS SQL SERVER Manual de Referencia para usuarios. Salomón Ccance CCANCE WEBSITE

Clases y Objetos. Informática II Ingeniería Electrónica

Los paquetes tienen dos partes: una especificación y un cuerpo que están almacenados por separado en la base de datos.

Capítulo 6. Introducción a la POO

MANUAL DE AYUDA TAREA PROGRAMADA COPIAS DE SEGURIDAD

ing Solution La forma más efectiva de llegar a sus clientes.

Curso PHP Módulo 1 R-Luis

Tutorial Servicios Web

INSTITUTO TECNOLOGICO de la laguna Programación Orientada a Objetos en C++

MANUAL DE EJECUCION DE LA ESTRATEGIA Ibex35 Evolución por Josep Codina

PHP y MySQL. Inicio: - Herencia - Palabra clave Final - Polimorfismo - Type Hinting - Abstracción de clases

Introducción a la Programación en MATLAB

1.1 Planteamiento del problema

1ª PARTE MANUAL BÁSICO DE POSICIONAMIENTO WEB 1 2ª PARTE MANUAL BÁSICO DE POSICIONAMIENTO WEB 7

Contenidos. Funciones (suplemento) Funciones. Justificación del uso de Funciones


Capítulo 4. Vectores y matrices. 4.1 Declaración de tablas. 4.2 Declaración estática de tablas

Sistema de Facturación de Ventas WhitePaper Enero de 2007

Aspectos a considerar en la adopción por primera vez en la transición a las NIIF para PYMES

PHP Perfect SQL v1.0 (SQL perfectas en PHP)

Consultas con combinaciones

Manual de referencia y del usuario

Este programa mueve cada motor de forma independiente, y cuando termina una línea pasa a la siguiente.

GENERACIÓN DE CÓDIGO

Lección 9: Polinomios

Manual de Timbrado FEL

El proceso de Instalación de Microsoft SQL Server 2008

QUÉ ES LA RENTABILIDAD Y CÓMO MEDIRLA. La rentabilidad mide la eficiencia con la cual una empresa utiliza sus recursos financieros.

UNIDAD 6. POLINOMIOS CON COEFICIENTES ENTEROS

TEMA 8: SISTEMA DE COSTES POR PROCESOS. INDICE. 1.- Caracteristicas generales de los sistemas de costes por procesos.

Capítulo 9. Archivos de sintaxis

Sesión 8 Sensor de Ultrasonido

Transcripción:

Convirtiendo arboles de expresión lambda en diferentes strings. Por: Horacio Aldo Tore, escrito en abril del 2010 en Buenos Aires Argentina Objetivo: Exponer en forma práctica con un ejemplo, como un mismo árbol de expresión (Expression tree) puede ser interpretado y convertido en distintas formas. Es con esta misma lógica como se desarrollo linq to SQL, linq to Dataset, linq to Entities y así varios proveedores más de linq to algo. La idea es escribir una sola expresión lambda de consulta en CSharp y que luego el proveedor de linq se encargue de transformarlo en una sentencia SQL válida para Oracle o para SQL Server dependiendo del proveedor de linq utilizado. Cabe destacar que no es objetivo de este documento detallar las propiedades de LINQ. Introducción: Las lambda pueden ser utilizadas como métodos anónimos o como arboles de expresión, trataremos aquí a las mismas como arboles de expresión, en otras palabras una estructura que representa en forma de árbol las operaciones y el orden en que deben realizarse. Por ejemplo el árbol de expresión dado por la lambda x => x * 4 + 1 no solo nos daría las operaciones matemáticas, factores y sumandos que la componen, sino también el orden en que dichas operaciones debe efectuarse para obtener el resultado correcto, en este caso primero el producto y luego la suma. Grafico del arbol de exprecion lambda x=> x * 4 + 1 Así la línea de código Expression<Func<int, int>> expression = x => x*4+1 genera una estructura de árbol de expresión en la cual lo importante no es darle un valor a x y obtener el resultado sino que lo importante es poder interpretar y obtener información de cómo está escrita esta operación matemática, que hay una multiplicación cuyos factores son un parámetro llamado x del tipo entero y cuyo otro factor es una constante, también entera, cuyo valor es 4, que la primera operación matemática que se debe realizar es el producto y así sucesivamente. Este árbol de expresión nos permitirá a los desarrolladores interpretarlo, navegarlo, serializarlo, ejecutarlo o transformarlo según nuestra conveniencia. La lambda x => x*4+1 por sí sola no nos brinda información alguna sobre su tipo, ya que puede ser del tipo Expression<Func<int, int>> o Func<int, int> razón por la cual el siguiente código fallará en tiempo de compilación e incluso antes, en tiempo de edición ya que el motor de IntelliSense detecta la ambigüedad establecida por la sobrecarga del método Convert o en otras palabras no se puede determinar cuál de las dos definiciones del método Convert debe ser invocada. El siguiente código emite este error: The call is ambiguous between the following methods or properties Page 1 of 6

private void MainX() Convert(x => x * 4 + 1); private void Convert(Expression<Func<int, int>> expressionx) private void Convert(Func<int, int> funx) Otro hecho de ambigüedad de características similares al anterior, es el siguiente: var lambdax = x => x * 4 + 1; La variable lambdax es de tipo implícita (implicit type) no es del tipo explicito (explicit type), esto quiere decir que su tipo será inferido del tipo de dato que se le esta asignado, por ejemplo en var nombre = "sadosky manuel"; la variable nombre tomara el tipo string dado que se le está asignado una constante de este tipo, cabe destacar que desde el momento en que la variable adopta el tipo, este es conservado durante todo el tiempo de vida de la misma sin poder cambiarse, ahora no se puede inferir el tipo de lambdax ya que no se puede determinar si es Expression<Func<int, int>> o Func<int, int>, razón que genera el siguiente error Cannot assign lambda expression to an implicitly-typed local variable. Desarrollo: Dada la breve introducción ya podemos ingresar al tema que nos proponemos tratar que es cómo convertir un mismo árbol de expresión lambda en diferentes strings. Convertiremos el mismo árbol de expresión lambda x = > x + 1 en dos string uno que nos de cómo resultado "x.add(1)" y otro "(x) + (1)", en la siguiente tabla veremos unos ejemplos más, para de esta forma lograr entender lo que se quiere plantear. árbol de expresión lambda Expression<Func<int, int>> expression = x => x + 1; Expression<Func<int, int>> expression = x => x + 1-4; Expression<Func<int, int>> expression = x => x + (1 - (x + 1)); Expression<Func<int, int, int>> expression = (x,y) => y + (1 - (x + 1)); Resultado de la conversión dada por el método ToNaturalFormat Resultado de la conversión dada por el método ToAnotherFormat "x.add(1)" "(x) + (1)" "x.add(1).subtract(4)" "((x) + (1)) - (4)" "x.add(1.subtract(x.add(1)))" "(x) + ((1) - ((x) + (1)))" "y.add(1.subtract(x.add(1)))" "(y) + ((1) - ((x) + (1)))" El siguiente fragmento de código muestra la creación de una expresión lambda y la posterior invocación a los métodos ToAnotherFormat y ToNaturalFormat responsables de las diferentes interpretaciones y conversiones: Main /// <summary> /// Método principal responsable de invocar a los métodos /// encargados de convertir la misma expresión lambda a dos formatos distintos. /// </summary> private void Main() Page 2 of 6

// Creacion de la exprecion lambda a convertir Expression<Func<int, int>> expression = x => x + 1; // Linea 0 // retorno esperado: x.add(1) string textinanotherformat = ToAnotherFormat(expression); // retorno esperado: (x) + (1) string textinnaturalformat = ToNaturalFormat(expression); Los métodos ToAnotherFormat y ToNaturalFormat son los responsables de interpretar el árbol de expresión y de convertirlo en un string, podemos ver este árbol en forma gráfica como sigue: Grafico del arbol de exprecion lambda x=> x + 1 Los métodos de conversión son recursivos y saben cómo convertir cada uno de los nodos que componen el árbol. Es necesario que el parámetro del método de conversión sea del tipo System.Linq.Expressions.Expression ya que es de este del que heredan todas las expresiones, como en este caso LambdaExpression, BinaryExpression etc. Podemos notar que los métodos de conversión aquí presentados no pueden resolver cualquier tipo de expresión lambda, por ejemplo si se pasa como parámetro la siguiente x => x / 1, el programa compilaría pero en tiempo de ejecución se dispararía una excepción del tipo NotSupportedException y cuya propiedad Message contendría el siguiente texto " El siguiente tipo no está soportado: Divide ", esto se debe a que el switch no contempla nodos del tipo ExpressionType.Divide, es buena práctica crear un caso de default en la sentencia switch para que contemple los tipos de nodo no implementados ya que de otra forma nuestra aplicación no daría excepción alguna pero si funcionaria en forma anómala. Podemos reemplazar la "// Linea 0" del método Main por alguna otra expresión lambda más compleja como ser Expression<Func<int, int, int>> expression = (x,y) => y + (1 - (x + 1)); y el programa seguirá funcionando correctamente dado el carácter genérico y recursivo de los métodos de conversión, la salida sería la que siguiente : "(y) + ((1) - ((x) + (1)))" y "y.add(1.subtract(x.add(1)))" causada por los métodos ToNaturalFormat y ToAnotherFormat respectivamente. Código del método ToNaturalFormat /// <summary> Page 3 of 6

/// Convierte la expresión lambda x = > x + 1 en un string de la forma "(x) + (1)" /// </summary> /// <param name="expression"> /// Arbol de expresión a convertir. /// </param> /// <returns> /// Resultado de la conversion. /// </returns> public string ToNaturalFormat(Expression expression) string text = ""; BinaryExpression binaryexpression; string textleft; string textright; switch (expression.nodetype) case ExpressionType.Lambda: LambdaExpression lambdaexpression = (LambdaExpression)expression; text = ToNaturalFormat(lambdaExpression.Body); case ExpressionType.Add: text = "(0) + (1)"; textleft = ToNaturalFormat(binaryExpression.Left); textright = ToNaturalFormat(binaryExpression.Right); case ExpressionType.Subtract: text = "(0) - (1)"; textleft = ToNaturalFormat(binaryExpression.Left); textright = ToNaturalFormat(binaryExpression.Right); case ExpressionType.Parameter: ParameterExpression parameterexpression = ((ParameterExpression)expression); text = parameterexpression.name; case ExpressionType.Constant: ConstantExpression constantexpression = ((ConstantExpression)expression); text = constantexpression.value.tostring(); default: throw new NotSupportedException("El siguiente tipo no esta soportado: " + expression.nodetype.tostring()); return text; Código del método ToAnotherFormat /// <summary> /// Convierte la expresión lambda x = > x + 1 en un string de la forma "x.add(1)" /// </summary> Page 4 of 6

/// <param name="expression"> /// Arbol de expresión a convertir. /// </param> /// <returns> /// Resultado de la conversion. /// </returns> public string ToAnotherFormat(Expression expression) string text = ""; BinaryExpression binaryexpression; string textleft; string textright; switch (expression.nodetype) case ExpressionType.Lambda: LambdaExpression lambdaexpression = (LambdaExpression)expression; text = ToAnotherFormat(lambdaExpression.Body); case ExpressionType.Add: text = "0.Add(1)"; textleft = ToAnotherFormat(binaryExpression.Left); textright = ToAnotherFormat(binaryExpression.Right); case ExpressionType.Subtract: text = "0.Subtract(1)"; textleft = ToAnotherFormat(binaryExpression.Left); textright = ToAnotherFormat(binaryExpression.Right); case ExpressionType.Parameter: ParameterExpression parameterexpression = ((ParameterExpression)expression); text = parameterexpression.name; case ExpressionType.Constant: ConstantExpression constantexpression = ((ConstantExpression)expression); text = constantexpression.value.tostring(); default: throw new NotSupportedException("El siguiente tipo no esta soportado: " + expression.nodetype.tostring()); return text; Conclusión del desarrollo: Volviendo a nuestro ejemplo, tratar de hacer un intérprete (parser) de expresiones matemáticas con solo sumas, restas y cualquier nivel de paréntesis que indican la prioridad de las operaciones a realizar hubiese sido muy difícil de desarrollar sin la existencia de los arboles de expresión lambda, imaginemos tener que interpretar algo tan simple como lo que sigue: y + (1 - (x + 1)) teniendo que identificar las operaciones, el orden en que se deben realizar las mismas y los posibles errores de sintaxis o escritura que pudieran haberse cometido. Es evidente el orden de los sumandos Page 5 of 6

no altera la suma (Ley conmutativa), pero recordemos que el objetivo de los arboles de expresión no es ejecutar el código sino describir con precisión como ha sido escrito este. Si la operación matemática está mal escrita, el código no compilará, razón por la cual no es necesario que verifiquemos la correcta escritura de la misma como ser por ejemplo la falta del cierre de un paréntesis o intentar sumar un valor que no es del tipo numérico, la definición de la expresión lambda dada por Expression<Func<int, int>> nos garantiza lo antes mencionado. Conclusión: Los arboles de expresión lambda son la solución al problema de interpretar pequeños trozos de código nativo, y hacer algún proceso a partir de dicha interpretación, como transformarlo en otros códigos compatibles con otras plataformas como lo hace LINQ to SQL que trasforma las lambda en sentencias de Transact-SQL o LINQ to Oracle que las transforma en PL/SQL, de esta forma las consultas a la base de datos serian independientes de la misma ya que estarían escritas en código nativo (por ejemplo CSharp de.net) lo que nos permitiría cambiar con facilidad de proveedor de base de datos si fuera necesario, con un impacto mínimo en el código de nuestro sistema. Otro punto de vista es que estamos escribiendo las consultas en forma declarativa (describimos la consulta) y no imperativa (como de debe hacerse la consulta), nuestro proveedor de LINQ será el encargado de transformar dicha descripción de la consulta en cómo debe hacerse la misma ya que la descripción de la consulta es la misma y es independientemente de la base de datos en que se debe ejecutar dicha consulta. Por ejemplo, describiremos un filtro con todos los usuarios cuya edad es mayor a 90 años y nuestro proveedor de LINQ to SQL se encargaría de trasformar esto a algo del estilo de SELECT * FROM usuarios WHERE edad > 90 y nuestro proveedor de LINQ to TXT se encargaría de y etc. Links utilizados para desarrollar este documento: http://msdn.microsoft.com/spain/library/bb384061.aspx Link Descripción Idioma http://www.clikear.com/c_desmitificando_expresiones_18846.aspx C#: Desmitificando las Español expresiones lambda (I) http://www.variablenotfound.com/2009/03/c-desmitificando-lasexpresiones-lambda_29.html C#: Desmitificando las expresiones lambda (II) Español http://www.variablenotfound.com/2009/03/c-desmitificando-lasexpresiones-lambda_2829.html http://marlongrech.wordpress.com/2008/01/08/working-withexpression-trees-part-1/ C#: Desmitificando las expresiones lambda (y III) Variables locales con asignación implícita de tipos (Guía de programación de C#) Working with Expression Trees Español Español Ingles Page 6 of 6