domingo, 15 de diciembre de 2019

Reglas de la Ingeniería de software (índice)


Cuando comencé este blog, lo hice con la intención de facilitar el trabajo de las personas que se dedican al desarrollo de software, de forma profesional. Creo que existen muchos blogs y libros tecnológicos que hablan desde el desarrollo de software desde un punto de vista académico y teórico, pero muy pocos que lo atacan desde un punto de vista laboral (sobre todo en español).

Desde hace bastantes años, tengo ciertas labores de “tutelaje”, intentando ayudar a nuevos profesionales, a que vean el panorama completo desarrollo de software empresarial. Con base a eso, comencé a elaborar una serie de “Reglas de la Ingeniería de Software”, puntos cuya lectura nos ayudarían a hacer software de calidad en un tiempo razonable. Se analizaba todo los que nos llevaba al éxito y los que nos llevaba al fracaso (y creedme he estado en ambos tipos de proyectos), y se intentaba sintetizar en un punto fácil de recordar.


Las reglas son de diversa índole, por que abarcan el desarrollo empresarial de software en general. Así que se puede encontrar reglas que hagan referencia a aspectos técnicos de los mismos lenguajes de programación, pero también a aspectos como la gestión del tiempo, la responsabilidad, o la relación entre los integrantes de un equipo de desarrollo.

Algunas reglas pueden parece que están repetidas, esto porque hay una constante que se da en el desarrollo empresarial, que es el cambio rápido y constante en el negocio que abarcan los sistemas, los que provoca cambios en los mismos sistemas, a sus capacidades y sus necesidades, forzándonos a que sea simple y fácil de mantener. Muchas reglas girar en torno a esas ideas, aunque enfocadas cada una desde un punto de vista diferente, aunque parecido.

Por otro lado este es un proyecto vivo, en el que se agregaran, corregirán (y quitaran si es necesario), las reglas que sean necesarias, para mantenerlas coherente y útiles para la vida laboral del desarrollador de software.

Las reglas actuales son las siguientes:

  • Regla N°1: Va a cambiar

    31 de agosto de 2014


    Todo cambia muy rápido en el proceso de desarrollo de un software, el mismo software construido, genera nuevos requisitos, la satisfacción de las necesidades de un proyecto, genera a su vez nuevas necesidades.

    Explicado de otra forma la resolución de un problema, genera un entendimiento más claro de dicho problema, y en esta situación, se abren nuevas posibilidades sobre las capacidades del software y el negocio que sustenta.

  • Regla N°2: Va a fallar

    6 de septiembre de 2014


    Es imposible determinar si cierto software continuara funcionando en un futuro, porque las condiciones en las que se verá envuelto son impredecibles (actualizaciones de software con el que convive, actualizaciones de hardware… incluso la fecha y hora del ordenador).

    Hay que evitar actitudes optimistas con respecto a la posibilidad que ocurran fallos, puesto que es muy probable que estos ocurran, por otro lado considerar que el usuario del sistema hará un uso “lógico” o coherente de este, es un desatino, generalmente nos sorprenderá descubriendo errores, que nunca se nos hubieran pasado por la cabeza.

  • Regla N°3: Se va a mantener

    15 de septiembre de 2014


    El tiempo que vas a estar creando software nuevo, es incomparablemente menor al tiempo que vas a estar manteniéndolo.

    Gran parte nuestros esfuerzos de desarrollo van a estar enfocados en el mantenimiento de código que bien puede ser código ajeno o código de otras personas.

  • Regla N°4: Todo sistema tiene un propósito

    21 de septiembre de 2014


    Es común que olvidemos que lo que estamos creando no es un conjunto de variables, y líneas de código, sino que tiene que un significado y relevancia especial fuera del lenguaje de programación en el que los estamos creando. Por ejemplo, una trasferencia bancaria de un millón de dólares errónea no es una posición de memoria mal inicializada, sino que puede ser una pérdida real, o un sistema médico de monitores mal construido puede poner en juego vidas humanas. Hay que pensar en nuestros sistemas por lo que van a poner aportar en un negocio o los problemas que van a poder resolver y no como simples entes tecnológicos.

  • Regla N°5: No te repitas

    28 de septiembre de 2014


    La regla "No te repitas", hace referencia a que no se deben repetir porciones de código, con funcionalidades iguales, "casi iguales", o parecidas a los largo del código. Aunque técnicamente funcione y de el resultado correcto, aumenta el grado de "degeneración del software" (el tiempo en que se echa a perder un software, hasta que ya no es útil su funcionalidad).

  • Regla N° 6: Lo importante es el "Que hace" y no el "Como lo hace"

    16 de octubre de 2014


    En la programación orientada a objetos, se diseñan elementos, donde prima la representación de "Que es lo que hace" y "Que son", principalmente, a través de sus atributos y sus métodos. también se establece como se relacionan con otros tipos de elementos (o clases), ya sea mediante técnicas como la composición, la herencia, o el envió y recepción de mensajes.

    De esta forma centrándonos en "Que hace" obtenemos una comprensión mas clara del sistema, y de sus requisitos, además construiremos un sistemas más escalable (debido a que no nos estamos centrando en "Como lo hace"). Inclusive tendremos muchas más posibilidades de conectarnos a otros sistemas, puesto que queda mas claramente establecidos los limites y conectores de estos.

  • Regla N°7: Primero los objetos, después las base de datos

    2 de enero de 2015


    El enfoque más idóneo para comenzar un sistema, es el diseño de los objetos o las clases de los que se va a constituir nuestros sistemas. Generalmente al comienzo del diseño, tenemos una idea de cómo queremos hacer las cosas, dicha idea no tiene que estar completa, y cuanto más avancemos en ella, mas compresión tendremos sobre la misma. Podemos ir reduciendo el nivel de abstracción según los necesitemos. Igualmente podemos usar relaciones de objetos, ya sea de jerarquía (herencia) o de contención (un objeto contiene a otro, o a varios). Y lo más importante, se comprende que los datos, van de la mano de las operaciones (funcionalidad), y se diseña el sistema de dicha forma.

  • Regla N°8: El entusiasmo da el conocimiento, el conocimiento sin entusiasmo no sirve de nada

    19 de julio de 2015


    El conocimiento sin entusiasmo es, en el mejor de los casos, fortuito, y casi nunca podrá convertirse en algo útil para el negocio, en sistemas y herramientas funcionalidades que creen una diferencia significativa. En la mayoría de los casos lo único que podrá realizarse sin entusiasmo es lo que yo llamo “tareas de escuela”, programas técnicamente correctos, hechos según un enunciado claro como una receta, pero que no sirve en la práctica de absolutamente nada, que son imposibles de encajar en un ambiente laboral real. En última instancia los conocimientos, y más en esta profesión, son caducos.

  • Regla N°9: Los warnings de hoy son los errores de mañana (programa sin warning)

    8 de agosto de 2015


    Cuando me enfrento al mantenimiento de código realizado por diversos equipos de trabajo (y he de reconocer que no con menos frecuencia al creado por mi), me encuentro con que al compilar, existe un excesivo número de advertencias (excesivo son más de 200 o incluso mas). Advertencias que se han ido creando a lo largo del tiempo y que nadie se ha molestado en revisar jamás, acumulándose más y más cuanto más codificadores mueven el sistema. Generalmente nadie toca dichos warning debido al temor que entraña "mover" algo cuyo sentido desconocemos (el hecho que lo desconozcamos ya debiera hacer saltar la alarma). Bueno, si existe el warning, seguramente no tenga ningún significado oculto y simplemente es una omisión no intencional que deba ser corregida.

    Los warnings se acumulan a lo largo de los años y el número dificulta entender claramente lo que nos están indicando, cuanto más warnings haya, menos casos les haremos, hasta que uno de esos warnings realmente representen un potencial problema en producción que se nos pase completamente desapercibido, convirtiéndose en un dolor de cabeza (con suerte) o en algo más grave, porque "los warnings de ahora son los errores de mañana".

  • Regla N°10: Si no es sencillo está mal hecho

    1 de diciembre de 2015


    El desarrollo de un software tiende a complicarse… a complicarse mucho. Se complica sacar el proyecto a tiempo, se complica las relaciones con el equipo de trabajo, con los proveedores o con los recursos. Debemos mantener esa complejidad bajo control, teniendo en la mente que todo lo que hagamos cumplir la regla "Si no es sencillo, está mal hecho".

    La sencillez muchas veces se confunde. Sencillez no quiere decir fácil, es casi siempre lo contrario. Es difícil hacer las cosas sencillas. Casi siempre invertir en sencillez es un proceso costoso al principio y ventajoso al final del proyecto. El objetivo de esta regla es conseguir sistemas que estén guiados por la sencillez.

  • Regla N°11: El primer código es para entender el problema, los restantes para hacerlo bien

    5 de febrero de 2017


    Como defensor del las metodologías agiles, creo que la mejor forma de comprender un problema es descomponiéndolo en problemas más sencillos y solucionándolos uno a uno.

    Esto implica que hasta que no tengamos algo de código listo, no comprenderemos exactamente la necesidad que estamos intentando suplir. Es más, el ver la necesidad resulta, pueda cambiar los requisitos y la percepción sobre el problema real a tratar.

    Los motivos anteriores hacen que el primer código no sea el más optimo, porque su objetivo está más orientado al análisis que a la implementación. La situación en este punto es tenemos un código que nos muestra una solución, pero es una solución temporal y estática (Si hubiera un cambio en los requisitos sería muy difícil de ajustar estar código para atenderlo).

    Es por lo cual necesitamos realizar una segunda revisión del código, en estas caso ya no para resolver el problema de negocio, sino para asegurarnos que nuestro código sea de calidad, cumpliendo los siguientes requisitos:

  • Regla N°12: “Hay que prepararse para la lluvia cuando hace sol” (olvídate de “Si funciona no lo toques”)

    16 de agosto de 2017


    Si funciona no lo toques”, es un refrán bastante popular y además es horrible, promueve una filosofía de conformismo e incluso de cobardía ante el cambio. Me imagino que funcionaria en algún momento, donde tu zona de confort sea enorme. En la actualidad, y particularmente en nuestro negocio, las zonas de confort tienen a hacerse muy pequeñas muy rápidamente.

    ¿Cuándo es el momento de “tocar” algo que funciona?, si crees que es “cuando no funcione”, estas en un error, ese es el peor momento para plantearte cambios. Por la misma circunstancia que “no funciona”, el objetivo, o la prioridad ya no es crear ningún tipo de mejora, si no conseguir que el sistema siga funcionando.

  • Regla N°13: Simplifica las cosas

    10 de noviembre de 2017


    Cuanto te enfrentes a un problema grande, simplifícalo en problemas más pequeños. Si estos problemas siguen siendo demasiado grandes repite el proceso hasta que tengas un problema lo suficientemente pequeño para poderlo resolver.

    Para simplificar partimos siempre de un problema grande que es irresoluble en su tamaño actual, e identificamos las partes que lo componen. Lo dividimos en pequeños problemas que son más fáciles de resolver, repitiendo el proceso hasta que podémonos identificar dichos problemas con una solución clara y sencilla, y además que solo sirva para resolver exclusivamente una necesidad.

  • Regla N°14: Evita la ceguera voluntaria

    16 de diciembre de 2017


    La ceguera voluntaria es ignorar de forma deliberada situaciones que si bien son perjudiciales, no suponen un daño inmediato, con lo que no se hace nada para corregirlas, convirtiéndose en una posible situación catastrófica a futuro.

    Cuando aparecen problemas en mitad de un desarrollo y no son atacados y mitigados cuando son pequeños, aumentaran de forma progresiva hasta ser de tal tamaño que afecte al tiempo, calidad o costo del proyecto.

  • Regla N°15: Haz que las cosas sucedan

    11 de enero de 2018


    Una de las cosas que más afectan al éxito del desarrollo de un sistema es la pasividad y falta de proactividad. Esto es, principalmente, quedarse esperando a que las cosas pasen por si solas.

    Muchas veces existen escenarios de incertidumbre en que no queda claro que es lo tenemos que hacer en cuanto al sistema que se está construyendo, esto puede ser por muchos motivos, puede ser una falta de visibilidad del objetivo real o que no se nos ha comunicado apropiadamente.

    Pero es necesario comprender que mientras el sistema no esté finalizado (satisfaga las necesidades del cliente), hay tareas pendientes que hay que resolver y es responsabilidad de cada integrante del equipo conocer cuáles son y conseguir se lleve a cabo. Es responsabilidad de cada uno hacer que las cosas sucedan.

  • Regla N°16: Por defecto todo es privado (principio de ocultación)

    25 de enero de 2018


    He observado un problema muy común de muchos programadores noveles, escriben la palabra "public" por inercia al momento de crear una nueva funcionalidad, sin pararse realmente a pensar que están haciendo, y solo cuando lo meditan bien escriben "privado". A veces no se dan cuenta de esta circunstancia, otra veces piensan que es más sencillo, si todo es público, a veces incluso piensan que le dan cierto sentido de "libertad", cuando lo que se hace abrir puerta al desastre (la “libertad” la da en todo caso, compartir el código fuente, no habilitar métodos privados como públicos).

  • Regla N°17: Primero el camino principal luego las excepciones

    23 de diciembre de 2018


    Si es bien es cierto que uno se tiene que prepara para los fallos (para que el software falle) la ruta principal (de ejecución) debe ser un camino limpio y fácilmente trazable. Incluso desde el momento del diseño, no podemos dejarnos abrumar con todas las excepciones y giros posibles de la lógica de negocio, debemos diseñar el camino feliz, y asumir que habrá excepciones en algún momento. Esto es porque si bien el camino principal suele ser sencillo, los recovecos, bifurcaciones y excepciones suelen ser muchas, muy variadas y a veces difíciles de ver, si el cambio principal y los secundarios están entrelazados obtendremos un código sumamente difícil de comprender.

  • Regla N°18: Mejor herencia que sentencias condicionales (evita el código espagueti)

    4 de febrero de 2019


    El código Espagueti es aquel que tiene un control de flujo demasiado complicado para entenderlo claramente, además de que son prácticamente imposibles de modificar por miedo a que mientras cambiamos algo se estropee otra cosa. El código esta enredado tal como un plato de espagueti, se llega a esta situación (en la actualidad), a base de agregar sentencias if encadenas, grandes y complejas. Ya de por sí, tener dos if encadenados aumenta la complejidad del código y la dificultad de mantenerlo.

    Para evitar que nuestro programa contenga código espagueti debemos evitar usar instrucciones condicionales, cuando estas pueden evitarse usando mecanismos de herencia, "Mejor herencia que condicionales".




Si te ha gustado la entrada, ¡Compártela! ;-)

miércoles, 6 de noviembre de 2019

Paradigmas y tipos de lenguajes informáticos. Un enfoque practico


En el 2019 decidí comenzar una serie de artículos sencillos para mi blog (Desde las Horas Extras) sobre tipos y paradigmas de programación, la intención era crear máximo unos tres artículos, ya la naturaleza del blog es tratar temas relativos a la programación y al desarrollo de software en la empresa.

Sin darme cuenta el número de artículos y su complejidad creció, y parecía que los artículos planeados se quedaban cada vez más cortos, al final me ocupo prácticamente todo el año, donde no hubo espacio para otros temas.

Todo eso se convirtió en este documento , donde se intenta hacer un recorrido sobre las diferentes formas de clasificar lenguajes de programación

Se intentó que todos los temas tratados, tuvieran impacto real en el desarrollo de software y pudieran usarse tanto para una capacitación académica, como para instruir y apoyar a alguien que ya desarrolla software de forma profesional.

El conjunto de los lenguajes que se ha seleccionado para los ejemplos son variados, y solo corresponde a un gusto personal, son mis lenguajes favoritos, o por lo menos con los que más he tenido que trabajar profesionalmente.


Las clasificaciones de los lenguajes han sido las siguientes:

  • Según el nivel.
  • Según la generación.
  • Lenguajes interpretados o compilados.
  • Estáticos o dinámicos
  • Según el “ Tipado ”.
  • Según el Paradigma.

En los datos históricos se ha intentado ser lo más rigoroso posible, aunque a veces es difícil precisar en qué momento exacto se introdujo una características en un lenguaje, que lo hace ser más de un tipo que de otro.

Sin embargo en todo momento se intenta mostrar los (tipos de) lenguajes de programación, no como una verdad absoluta, si no como algo que ha fluido a lo largo del tiempo, adaptándose a las necesidades (empresariales) de cada momento.

Acerca de este documento


Este documento tiene licencia GPL ( GNU Lesser General Public License v3.0), en la práctica es un documento creado, sin ánimo de lucro, que puedes usar tanto académicamente, como profesionalmente, sin ninguna restricción más que las indicadas en la misma licencia (que básicamente es mencionar el copyright y conservar la licencia).

Todo el código fuente ha sido desarrollado para ejemplificar los temas aquí tratados, y tienen la misma licencia.

Las imágenes ilustrativas han sido en su mayoría encontradas en internet, en lugar libres de derechos, pero por si error se incluyó una imagen y consideras que se está haciendo un uso indebido de ella, por favor comunícalo, y procederemos a retirarla del documento.

Este documento está (junto el código fuente) en un repositorio de GitHub en:


Son bien recibidos las correcciones y nuevos ejemplos y temas, tanto en código, como en el mismo documento.

El documento es un compendio de los artículos publicados en el blog de autor, https://desdelashorasextras.blogspot.com/, sobre tipos y paradigmas en los diferentes lenguajes de programación.

Enlaces online de este documento


Documento en PDF


Enlaces en el Blog

Codigo fuente de los ejemplos (y del documento)

Codigo fuente de ejemplo


  • Código fuente de los ejemplos (y del documento)



  • Northwind.sql

ParadigmasTiposLenguajes /Base de Datos/

En la carpeta Base de Datos encontrara el script para crear la base de datos Northwind, es una base de datos de Microsoft que se usa en algunos ejemplos de este documento, puede instalar una versión de SQL Server Express e implementarla allí

A continuación las carpetas donde se encuentra el código fuente ilustrativo de los capítulos:


  • Capítulo VII. Combinaciones de lenguajes estáticos/dinámicos y débiles/fuertes

    ParadigmasTiposLenguajes / Fuentes /Tipado/

  • Capítulo IX. Paradigma Imperativo

    ParadigmasTiposLenguajes / Fuentes /Imperativo/

  • Capítulo X. Paradigma orientado a objetos

    ParadigmasTiposLenguajes / Fuentes / POO

  • Capítulo XI. Programación declarativa

    ParadigmasTiposLenguajes / Fuentes /Declarativo/

  • Capítulo XII. Programación declarativa en lenguajes empresariales

    ParadigmasTiposLenguajes / Fuentes /Multiparadigma/


Contenido del documento



  • Capítulo I. Paradigmas y tipos de lenguajes informáticos
  • Capítulo II. Clasificación según el nivel
  • Capítulo III. Clasificación según la generación
  • Capítulo IV. Lenguajes interpretados o compilados
  • Capítulo V. Lenguajes estáticos y dinámicos
  • Capítulo VI. Clasificado según el “Tipado”
  • Capítulo VII. Combinaciones de lenguajes estáticos/dinámicos y débiles/fuertes
  • Capítulo VIII. Clasificación según el paradigma
  • Capítulo IX. Paradigma Imperativo
  • Capítulo X. Paradigma orientado a objetos
  • Capítulo XI. Programacióndeclarativa
  • Capítulo XII. Programacióndeclarativa en lenguajes empresariales
  • Anexo I: Mapa
  • Anexo I: Codigo fuente de ejemplo
  • Anexo II: Enlaces online de este documento



Si te ha gustado la entrada, ¡Compártela! ;-)

Nota: Puedes encontrar todo el código fuente de este artículo en https://github.com/jbautistamartin/ParadigmasTiposLenguajes

miércoles, 30 de octubre de 2019

Paradigmas y tipos de lenguajes informáticos (3 de 3). Programación declarativa en lenguajes empresariales


Aunque casi todos los lenguajes empresariales se presentaban como lenguajes multiparadigma, lo cierto es que casi siempre eran lenguajes que admitían programación imperativa (con alguna de sus evoluciones) junto a programación orientada a objetos, sin acercase a algún otro tipo de paradigma. Esto cambio en los últimos años donde se incorpora, en los lenguajes de programación funciones y sintaxis declarativas que facilitan el desarrollo de software.


La programación declarativa se basa en crear código que expresa las necesidades que queremos resolver y cómo resolverlas (mas información en nuestra entrada anterior), a diferencia de la programación imperativa que se basa en seguir diversos pasos en los que indicamos como resolver un problema. Es por lo tanto mucho mas practico (y sencillo) un código declarativo (en que queda claro el problema), que uno imperativo para el tenemos que hacer un análisis más profundo para comprenderlo.


Como curiosidad, la programación declarativa es anterior a la programación imperativa, entonces ¿Por qué se impuso la programación imperativa, sobre la declarativa entonces? Fueron motivos comerciales más que nada, la programación declarativa tenía un deje mas científico que practico (de origen), y la programación imperativa se ajustaba mas a la comprensión del cómo funcionaba una maquina (considerando sus capacidades de procesamiento y su velocidad). Parecía más sencillo de comprender programas que indicaban una instrucción tras otra. A esto se debe añadir lo difícil de construir compiladores, ya que no se poseían las herramientas (ni las metodologías de análisis), actuales. En un mercado emergente como la computación, que se comenzaba a incorporar a las empresas, perecía más razonable diseñar lenguajes imperativos que sirvieran para vender computadoras, que lenguajes declarativos.


Un ejemplo de lo anterior es COBOL, que tiene un origen muy peculiar. Es uno de los lenguajes más antiguos existentes (del 1959). En su momento empresas como IBM estaban viendo la viabilidad de vender comercialmente y de forma masiva sus computadoras a negocios que necesitaban grandes herramientas de procesamiento de información, tal como bancos. El problema es que esas maquinas, eran monstruos enormes y terriblemente caras, y los directores de los bancos que autorizaban dichas compras no se sentían felices pagando dichas millonadas, para algo que difícilmente entendía (y para colmo tampoco entendían a los primeros informativos y matemáticos que si las podían manejar). La solución fue COBOL, un lenguaje que “parecía”, que era como escribir inglés, y con el que directores bancarios creían que podían estar más cerca de cómo funcionaban dichas maquinas, sintiéndose mas cómodos al desembolsar lo que constaba una computadora de la época (evidentemente todo era un efecto placebo, independiente de la potencia y capacidades de COBOL, solo querían saber que pudieran comprenderlo, aunque evidentemente no lo hacían). Esto es como un ejemplo de cómo un lenguaje imperativo ayudo a la venta comercial de computadoras.


En lo personal creo que todavía en la actualidad es inviable crear un sistema empresarial usando completamente un paradigma declarativo, pero realmente algunas partes es mejor crearlas de forma declarativa por su sencillez y claridad, es aquí donde se pueden mezclar la programación imperativa, la programación orientada a objetos, y la programación declarativa, para usar lo mejor de cada uno, según la necesidad.

A continuación algunos ejemplos de programación declarativa en lenguajes con enfoque empresarial.

Ruby


Ruby es posiblemente unos de mis lenguajes preferidos, tuvo un auge muy importante a principios de siglo, aunque en esta década (a partir de 2010) comenzó a decaer, frente a su principal competidor Python.


Ruby es un lenguaje multiparadigma, con una inspiración declarativa muy fuerte, como vemos en el siguiente comentario de su creador Yukihiro "Matz" Matsumoto:

A menudo la gente, especialmente los ingenieros en computación, se centran en las máquinas. Ellos piensan, "Haciendo esto, la máquina funcionará más rápido. Haciendo esto, la máquina funcionará de manera más eficiente. Haciendo esto..." Están centrados en las máquinas, pero en realidad necesitamos centrarnos en las personas, en cómo hacen programas o cómo manejan las aplicaciones en los ordenadores. Nosotros somos los jefes. Ellos son los esclavos.

Características declarativas de Ruby


Facilidad para expresar nuestras necesidades.


Ruby tiene muchas facilidades para expresar lo que necesitamos de una forma clara y con pocas líneas de código:

  • Declaración de variables

    En Ruby siempre es necesario asignar un valor a una variable, al momento de declararla. El tipo no se indica explícitamente, si no que se toma del valor de la variable.

    Asignación Ruby
     
    1
    2
    3
    4
    5
    6
    # *****************************************************
    # En  Ruby siempre es necesario asignar un valor a una variable, al momento de declararla.
    # El tipo no se indica explícitamente, si no que se toma del valor de la variable.
    # *****************************************************
     
    mensaje = 'Hola Mundo!!!'
     
     
    # *****************************************************
    # En  Ruby siempre es necesario asignar un valor a una variable, al momento de declararla.
    # El tipo no se indica explícitamente, si no que se toma del valor de la variable.
    # *****************************************************
    
    mensaje = 'Hola Mundo!!!'
    

  • Para declara un array simplemente se lo asignamos a la variable.

    Declaración Array
     
    1
    2
    3
    4
    5
    6
    7
    8
    # *****************************************************
    # Para declara un array simplemente se lo asignamos a la variable.
    # *****************************************************
     
    mensajes = ['Hola Juan', 'Hola Maria', 'Hola Pedro']
     
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro"]
     
     
    # *****************************************************
    # Para declara un array simplemente se lo asignamos a la variable.
    # *****************************************************
    
    mensajes = ['Hola Juan', 'Hola Maria', 'Hola Pedro']
    
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro"]
    

  • Para declarar un Dictionary (tambien llamdo Hash o Map, solo declaramos los valores y las llaves:

    Declaración de Hash
     
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    # Hash vacio
     
    hash = {}
     
    # Hash con valores
    hash =  {
                saludo_juan: 'Hola juan',
                saludo_maria: 'Hola Maria',
                saludo_pedro: 'Hola pedro',
            }
             
    puts hash.inspect
    # Salida: {:saludo_juan=>"Hola juan", :saludo_maria=>"Hola Maria", :saludo_pedro=>"Hola p
     
     
    # Hash vacio
    
    hash = {}
    
    # Hash con valores
    hash =  { 
                saludo_juan: 'Hola juan',
                saludo_maria: 'Hola Maria',
                saludo_pedro: 'Hola pedro',
            }
            
    puts hash.inspect
    # Salida: {:saludo_juan=>"Hola juan", :saludo_maria=>"Hola Maria", :saludo_pedro=>"Hola p
    

    Salida: ["Hola Juan", "Hola Maria", "Hola Pedro"]

  • Agregar un elemento a un array

    Agregar un elemento a un array
     
    1
    2
    3
    4
    5
    6
    7
    8
    # *****************************************************
    # Agregar un elemento a un array
    # *****************************************************
     
    mensajes << 'Hola Jose'
     
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose"]
     
     
    # *****************************************************
    # Agregar un elemento a un array
    # *****************************************************
    
    mensajes << 'Hola Jose'
    
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose"]
    

    Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose"]

  • Agrego un elemento en una posición aleatoria

    Agrego un elemento en una posición aleatoria
     
    1
    2
    3
    4
    5
    6
    7
    8
    # *****************************************************
    # Agrego un elemento en una posición aleatoria
    # *****************************************************
     
    mensajes[6] = 'Hola Elias'
     
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", nil, nil, "Hola Elias"]
     
     
    # *****************************************************
    # Agrego un elemento en una posición aleatoria
    # *****************************************************
    
    mensajes[6] = 'Hola Elias'
    
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", nil, nil, "Hola Elias"]
    

    Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", nil, nil, "Hola Elias"]

  • Agrego un elemento al final

    Agrego un elemento al final
     
    1
    2
    3
    4
    5
    6
    7
    8
    # *****************************************************
    # Agrego un elemento al final
    # *****************************************************
     
    mensajes << 'Hola Gustavo'
     
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", nil, nil, "Hola Elias", "Hola Gustavo"]
     
     
    # *****************************************************
    # Agrego un elemento al final
    # *****************************************************
    
    mensajes << 'Hola Gustavo'
    
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", nil, nil, "Hola Elias", "Hola Gustavo"]
    

    Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", nil, nil, "Hola Elias", "Hola Gustavo"]

  • Agrego un elemento al principio

    Agrego un elemento al principio
     
    1
    2
    3
    4
    5
    6
    7
    8
    # *****************************************************
    # Agrego un elemento al principio
    # *****************************************************
     
    mensajes.unshift('Hola Roberto')
     
    puts mensajes.inspect
    # Salida: ["Hola Roberto", "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", nil, nil, "Hola Elias", "Hola Gustavo"]
     
     
    # *****************************************************
    # Agrego un elemento al principio
    # *****************************************************
    
    mensajes.unshift('Hola Roberto')
    
    puts mensajes.inspect
    # Salida: ["Hola Roberto", "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", nil, nil, "Hola Elias", "Hola Gustavo"]
    

    Salida: ["Hola Roberto", "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", nil, nil, "Hola Elias", "Hola Gustavo"]

  • Buscar elementos

    Buscar un elemento, aquellos mensajes que acaben con la letra o, aquí se usan expresiones lamba, las veremos después

    Buscar un elemento
     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # *****************************************************
    # Buscar un elemento
    # Busco aquellos mensajes que acaben con la letra o, aquí se usan expresiones lamba, las veremos después
    # *****************************************************
     
    encontrados=mensajes.select {|e| e=~/o$/}
     
    puts encontrados.inspect
    # Salida: ["Hola Roberto", "Hola Pedro", "Hola Gustavo"]
     
     
    # *****************************************************
    # Buscar un elemento
    # Busco aquellos mensajes que acaben con la letra o, aquí se usan expresiones lamba, las veremos después
    # *****************************************************
    
    encontrados=mensajes.select {|e| e=~/o$/}
    
    puts encontrados.inspect
    # Salida: ["Hola Roberto", "Hola Pedro", "Hola Gustavo"]
    

    Salida: ["Hola Roberto", "Hola Pedro", "Hola Gustavo"]

  • Eliminar elementos de un array (por ejemplo los nulos)

    Eliminar elementos de un array
     
    1
    2
    3
    4
    5
    6
    7
    8
    # *****************************************************
    # Eliminar elementos de un array (por ejemplo los nulos)
    # *****************************************************
     
    mensajes.delete_if {|e| !e }
     
    puts mensajes.inspect
    # Salida: ["Hola Roberto", "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo"]
     
     
    # *****************************************************
    # Eliminar elementos de un array (por ejemplo los nulos)
    # *****************************************************
    
    mensajes.delete_if {|e| !e } 
    
    puts mensajes.inspect
    # Salida: ["Hola Roberto", "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo"]
    

    Salida: ["Hola Roberto", "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo"]

  • Trabajar con un array como una cola (último en entrar último en salir)

    Colas
     
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    # *****************************************************
    # Trabajar con un array como una cola (ultimo en entrar ultimo en salir)
    # *****************************************************
     
    mensajes << 'Hola Edgar'
    mensajes << 'Hola Luz'
     
    puts mensajes.inspect
    # Salida: ["Hola Roberto", "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz"]
     
    mensajes.shift()
     
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz"]
     
     
    # *****************************************************
    # Trabajar con un array como una cola (ultimo en entrar ultimo en salir)
    # *****************************************************
    
    mensajes << 'Hola Edgar'
    mensajes << 'Hola Luz'
    
    puts mensajes.inspect
    # Salida: ["Hola Roberto", "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz"]
    
    mensajes.shift()
    
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz"]
    

    Salida: ["Hola Roberto", "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz"]

    Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz"]

  • Trabajar con un array como una pila (ultimo en entrar, primero en salir)

    Colas
     
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    # *****************************************************
    #Trabajar con un array como una pila (ultimo en entrar, primero en salir)
    # *****************************************************
     
    mensajes.push('Hola Ruben')
     
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz", "Hola Ruben"]
     
    mensajes.pop()
     
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz"]
     
     
    # *****************************************************
    #Trabajar con un array como una pila (ultimo en entrar, primero en salir)
    # *****************************************************
    
    mensajes.push('Hola Ruben')
    
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz", "Hola Ruben"]
    
    mensajes.pop()
    
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz"]
    

    Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz", "Hola Ruben"]

    Salida: ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar", "Hola Luz"]

Como vemos todo el tratamiento de los arrays es bastante declarativo, parecido a PROLOG y LISP, en ningún caso debemos preocuparnos por cómo se almacenan los elementos del array, ni en tareas tales como reservar memoria o liberarla. Tampoco debemos conocer estructuras específicas para cada tarea propias de la programación orientada a objetos como Listas, Pilas, o Colas.

Bloques que contiene código


Ruby da una mezcla muy buena entre programación orientada a objetos y programación declarativa. Básicamente casi todo está orientado a objetos, tanto es así que no existen estructuras como los bucles for para recorrer colecciones, existe una método each (de la colección) y recibe como parámetro un función que será ejecutada por cada elemento de la colección.

La función que ejecutar el método recibe el nombre de bloque y puede ser especificada de una forma declarativa entre llaves o entre las clausulas do/ end .

  • Bloque especificado por llaves

    Imprimir el cuadro de cada numero
     
    1
    2
    3
    4
    5
    6
    7
    #Imprimir el cuadro de cada numero
     
    [1,2,3].each { |x| puts x*2
    # Salida:
    # 2
    # 4
    # 6
     
     
    #Imprimir el cuadro de cada numero
    
    [1,2,3].each { |x| puts x*2 }  
    # Salida: 
    # 2
    # 4
    # 6
    

  • Bloque do/end

    Otra forma semejante a la anterior
     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # Otra forma semejante a la anterior
     
    [1,2,3].each do |x|
      puts x*2                   
    end
    # Salida:
    # 2
    # 4
    # 6
     
     
    # Otra forma semejante a la anterior
    
    [1,2,3].each do |x|
      puts x*2                    
    end
    # Salida: 
    # 2
    # 4
    # 6
    

  • Variable que almacena un bloque

    Como muestra de lo anterior es posible asociar un bloque a una variable y pasársela a un método each, para que se ejecute por cada uno de los elementos de la colección.

    rear variables que almancene un proceso
     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    # Crear variables que almancene un proceso
     
    p = Proc.new { |x| puts x*2 }
    [1,2,3].each(&p)            
     
    # Salida:
    # 2
    # 4
    # 6
     
     
    # Crear variables que almancene un proceso
    
    p = Proc.new { |x| puts x*2 }
    [1,2,3].each(&p)             
    
    # Salida: 
    # 2
    # 4
    # 6
    

    Nótese que la variable solo almacena el código que se va a ejecutar pero que no se ejecuta realmente hasta que dicha variable se pasa como parámetro al each. También es de apreciar que todos los métodos son equivalentes.

  • Comprobar si existe un valor en un array (mediante bloques).


    Comprobar si un elemento existe en un array
     
    1
    2
    3
    4
    5
    6
    7
    8
    # *****************************************************
    # Comprobar si un saludo elemento existe en un array
    # *****************************************************
     
    mensajes = ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar"]
     
    puts( mensajes.any? {|e| e=~ /Maria/} )
    # Salida: true
     
     
    # *****************************************************
    # Comprobar si un saludo elemento existe en un array
    # *****************************************************
    
    mensajes = ["Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar"]
    
    puts( mensajes.any? {|e| e=~ /Maria/} )
    # Salida: true
    

  • Eliminar elementos de un array (mediante bloques).

    Eliminar elementos de un array
     
    1
    2
    3
    4
    5
    6
    7
    8
    # *****************************************************
    # Eliminar elementos de un array (elimino a Maria y a Jose)
    # *****************************************************
     
    mensajes.delete_if {|e| e=~ /Maria|Jose/ }
     
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Pedro", "Hola Elias", "Hola Gustavo", "Hola Edgar"]
     
     
    # *****************************************************
    # Eliminar elementos de un array (elimino a Maria y a Jose)
    # *****************************************************
    
    mensajes.delete_if {|e| e=~ /Maria|Jose/ } 
    
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Pedro", "Hola Elias", "Hola Gustavo", "Hola Edgar"]
    

  • Cambiar un array a masculas (mediante bloques).

    Eliminar elementos de un array
     
    1
    2
    3
    4
    5
    6
    7
    8
    # *****************************************************
    # Eliminar elementos de un array (elimino a Maria y a Jose)
    # *****************************************************
     
    mensajes.delete_if {|e| e=~ /Maria|Jose/ }
     
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Pedro", "Hola Elias", "Hola Gustavo", "Hola Edgar"]
     
     
    # *****************************************************
    # Eliminar elementos de un array (elimino a Maria y a Jose)
    # *****************************************************
    
    mensajes.delete_if {|e| e=~ /Maria|Jose/ } 
    
    puts mensajes.inspect
    # Salida: ["Hola Juan", "Hola Pedro", "Hola Elias", "Hola Gustavo", "Hola Edgar"]
    

C#


C# es un lenguaje que en los últimos tiempos ha tenido una evolución muy rápida, nació con un paradigma orientado a objetos, muy parecido a Java, y ha sabido ganarse su identidad, agregando multitud de funcionalidades declarativas que lo hacen un lenguaje claro y sencillo.


Características declarativas de C#


Facilidad para declarar variables


En momento de la creación de variables se ven ciertos aspectos declarativos, como por ejemplo:

  • Declaración de arrays

    Declaración de Arrays
     
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // Puede probar este codigo en https://repl.it/languages/csharp
     
    using System;
     
     
    public static class Program
    {
        private static void Imprimir(this string[] arrString)
        {
            Console.WriteLine($"[ \"{String.Join("\", \"", arrString)}\"] ");
        }
     
        public static void Main()
        {
            string[] mensajes = new string[] { "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar" };
     
            //Salida: [ "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar"]
     
            mensajes.Imprimir();
            Console.ReadLine();
        }
    }
     
     
    // Puede probar este codigo en https://repl.it/languages/csharp
    
    using System;
    
    
    public static class Program
    {
        private static void Imprimir(this string[] arrString)
        {
            Console.WriteLine($"[ \"{String.Join("\", \"", arrString)}\"] ");
        }
    
        public static void Main()
        {
            string[] mensajes = new string[] { "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar" };
    
            //Salida: [ "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar"]
    
            mensajes.Imprimir();
            Console.ReadLine();
        }
    }
    

    Salida: [ "Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar"]

  • Declaración de diccionarios

    Declaración de Diccionarios
     
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    // Puede probar este codigo en https://repl.it/languages/csharp
     
    using System;
    using System.Collections.Generic;
    using System.Linq;
     
    public static class Program
    {
        private static void Imprimir(this Dictionary<string, string> dicionario)
        {
            var lines = dicionario.Select(e => $"\t{e.Key}: {e.Value}");
     
            Console.WriteLine($"[{Environment.NewLine}{String.Join(Environment.NewLine, lines)}{Environment.NewLine}]");
        }
     
        public static void Main()
        {
            Dictionary<string, string> mensajes = new Dictionary<string, string>
            {
                ["saludo_juan"] = "Hola juan",
                ["saludo_maria"] = "Hola Maria",
                ["saludo_pedro"] = "Hola pedro",
            };
     
            //Salida:
            /*
             * [
             *   saludo_juan: Hola juan
             *   saludo_maria: Hola Maria
             *   saludo_pedro: Hola pedro
             * ]
             */
     
            mensajes.Imprimir();
            Console.ReadLine();
        }
    }
     
     
    // Puede probar este codigo en https://repl.it/languages/csharp
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    
    public static class Program
    {
        private static void Imprimir(this Dictionary<string, string> dicionario)
        {
            var lines = dicionario.Select(e => $"\t{e.Key}: {e.Value}");
    
            Console.WriteLine($"[{Environment.NewLine}{String.Join(Environment.NewLine, lines)}{Environment.NewLine}]");
        }
    
        public static void Main()
        {
            Dictionary<string, string> mensajes = new Dictionary<string, string>
            {
                ["saludo_juan"] = "Hola juan",
                ["saludo_maria"] = "Hola Maria",
                ["saludo_pedro"] = "Hola pedro",
            };
    
            //Salida:
            /*
             * [
             *   saludo_juan: Hola juan
             *   saludo_maria: Hola Maria
             *   saludo_pedro: Hola pedro
             * ]
             */
    
            mensajes.Imprimir();
            Console.ReadLine();
        }
    }
    

  • Declaración mediante el uso de var

    Existe una clausula especial para declarar variables sin necesidad de especificar el tipo:

    Uso de clausula var
     
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    using System;
     
    public static class Program
    {
        public static void Main()
        {
            var mensaje = "Hola mundo";
            var mensajes = new string[]{"Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar"};
            var mensajesUnidos = String.Join("[\", \"", mensajes + "]");
            Console.ReadLine();
        }
    }
     
     
    using System;
    
    public static class Program
    {
        public static void Main()
        {
            var mensaje = "Hola mundo";
            var mensajes = new string[]{"Hola Juan", "Hola Maria", "Hola Pedro", "Hola Jose", "Hola Elias", "Hola Gustavo", "Hola Edgar"};
            var mensajesUnidos = String.Join("[\", \"", mensajes + "]");
            Console.ReadLine();
        }
    }
    

    Con esto se infiere el tipo (en tiempo de compilación) de la variable, en este caso son String y String[ ].

    En lo personal no me gusta un uso excesivo de var, ya que no queda claro el tipo de la variable(al momento de leer el código), sobre todo si es el resultado deuda función. Pero esto cambia cuando estamos funciones Linq, donde sin duda, es mucho más sencillo usar la cláusula var, que especificar el tipo.

Hasta ahora hemos visto algunos tipos de declaración, que nos ayudan a especificar la creación de objetos de forma más declarativo que imperativa, aunque es, desde luego, mucho menos declarativo que Ruby.

Expresiones Lamba


Clásicamente, en la programación imperativa, se tienen variables que apuntan a funciones (en lugar de a datos), de forma que se pueden almacenar la función que se va a llamar en una variable, y para realizar la llamada posteriormente, por ejemplo en C, seria de la siguiente forma:

Puntero a funciones
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
 
int sumar_por_2(int numero){
  //Suma dos al número especificado
  return numero + 2;
}
 
int multiplar_por_2(int numero){
  //Multiplica por dos el número especificado
  return numero *  2;
}
 
 
int main(void) {
 
  int valor=5;
 
  //Puntero a funcion que devuelve recibe un int y devuelve un int
  int (*funcion) (int);
 
  //La función apunta a sumar 2
  funcion = &sumar_por_2;
 
  printf("Valor función: %d\n",funcion(valor));
 
  //La función apunta a sumar 2
  funcion = &multiplar_por_2;
 
  printf("Valor función: %d\n",funcion(valor));
 
  // Salida:
  /*
  * Valor función: 7
  * Valor función: 10
  */ 
   
  return 0;
}
 
 
#include <stdio.h>

int sumar_por_2(int numero){
  //Suma dos al número especificado
  return numero + 2;
}

int multiplar_por_2(int numero){
  //Multiplica por dos el número especificado
  return numero *  2;
}


int main(void) {

  int valor=5;

  //Puntero a funcion que devuelve recibe un int y devuelve un int
  int (*funcion) (int);

  //La función apunta a sumar 2
  funcion = &sumar_por_2;

  printf("Valor función: %d\n",funcion(valor));

  //La función apunta a sumar 2
  funcion = &multiplar_por_2;

  printf("Valor función: %d\n",funcion(valor));

  // Salida:
  /*
  * Valor función: 7 
  * Valor función: 10
  */  
  
  return 0;
}

De origen Java, elimino esta funcionalidad por lo que no era posible tener variables que apuntaran a código, si no que era necesario tener un objeto, que contuviera el código que queríamos ejecutar, a continuación un ejemplo de swing, donde se ve la implementación mediante Listeners (se muestra un mensaje por pantalla, al pulsar un botón).

Nótese que para el botón “uno” se usa programación orientada a objetos para indicar que debe hacerse al pulsar dicho botón, creando una clase anónima Listener. Se incluye un botón “dos” que hace lo mismo mediante programación declarativa y expresiones lamba, es, como se ve, mucho menos código y mas compresible,

Ejemplo Listener
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package com.desdelashorasextras;
 
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
 
public class Codigo11 {
    JFrame frame;
    JButton boton1;
    JButton boton2;
 
    Codigo11() {
        frame = new JFrame();
        boton1 = new JButton("Pulsa el boton Uno !!");
        boton2 = new JButton("Pulsa el boton Dos!!!");
 
        frame.setSize(550, 500);
        frame.setLayout(null);
        frame.add(boton1);
        frame.add(boton2);
 
        boton1.setBounds(10, 100, 200, 60);
        //Configuro un evento de forma tradicional para cuando el usuario haga clic,
        //se ve que necesito crear una clase anonima que capture el evento
        boton1.addActionListener( ( new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(frame, "Gracias desde el UNO!!");
            }
        }));
 
        boton2.setBounds(250, 100, 200, 60);
        //Misma situacion que la anterior pero usando programación declarativa
        //Disponible en las versiones modernas de Java
        boton2.addActionListener(e -> JOptionPane.showMessageDialog(frame, "Gracias desde el UNO!!"));
 
 
        frame.setVisible(true);
    }
 
    public static void main(String[] args) {
        new Codigo11();
    }
}
 
 
package com.desdelashorasextras;

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class Codigo11 {
    JFrame frame;
    JButton boton1;
    JButton boton2;

    Codigo11() {
        frame = new JFrame();
        boton1 = new JButton("Pulsa el boton Uno !!");
        boton2 = new JButton("Pulsa el boton Dos!!!");

        frame.setSize(550, 500);
        frame.setLayout(null);
        frame.add(boton1);
        frame.add(boton2);

        boton1.setBounds(10, 100, 200, 60);
        //Configuro un evento de forma tradicional para cuando el usuario haga clic,
        //se ve que necesito crear una clase anonima que capture el evento
        boton1.addActionListener( ( new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(frame, "Gracias desde el UNO!!");
            }
        }));

        boton2.setBounds(250, 100, 200, 60);
        //Misma situacion que la anterior pero usando programación declarativa
        //Disponible en las versiones modernas de Java
        boton2.addActionListener(e -> JOptionPane.showMessageDialog(frame, "Gracias desde el UNO!!"));


        frame.setVisible(true);
    }

    public static void main(String[] args) {
        new Codigo11();
    }
}

Java estaba en lo correcto al usar un paradigma orientado a objetos puro, donde incluso las llamadas a código mediante variables tenían que pasar por un Listener o alguna estructura semejante, pero eso hacia el código demasiado largo, enredoso, y difícil de comprender. En versiones posteriores Java introdujo las expresiones lambda y comenzó a añadir funciones declarativas y de otra índole multiparadigma.

C# (oficialmente inspirado en C++, pero en muchos aspectos claramente basada en Java) introdujo desde su primera versión la posibilidad de que las variables apuntaran a direcciones de código (a funciones directamente) de una forma bastante imperativa al principio (mediante delegados y delegados anónimos), hasta convertirse en algo mucho más declarativo (mediante expresiones lambda)

Veamos directamente el uso de expresiones lambda de forma declarativa:

  • Calculo del cuadro de un array de enteros

    Cuadrado de cada número
     
    1
    2
    3
    4
    5
    6
    7
    8
    int[] numeros = new int[] { 1, 2, 3 };
     
    // Imprimir el cuadro de cada numero
    var cuadrados = numeros.Select(x => x * x);
     
    //Salida: ["1", "4", "9"]
     
    cuadrados.Imprimir();
     
     
    int[] numeros = new int[] { 1, 2, 3 };
    
    // Imprimir el cuadro de cada numero
    var cuadrados = numeros.Select(x => x * x);
    
    //Salida: ["1", "4", "9"]
    
    cuadrados.Imprimir();
    

    El método Select, selecciona los elementos de un array, aplicándoles una posible transformación, que viene especificado mediante una expresión lamba. En este caso por cada x recibida (que es cada uno de los elementos del array), lo multiplica por sí mismo.

    x=> x * x, en esta expresión x es algo así como el parámetro de la expresión lamba y x * x el resultado.

    Otro detalle interesante, es el método Select, no pertenece al tipo de datos int [] sino que es un método extensor. Los métodos extensores son métodos que se definen fuera de la definición de la clase, y amplían las capacidades de esta sin modificarla, a veces incluso son aplicables a interfaces, por ejemplo, este es el método extensor que nos permite imprimir el resultado de los cuadrados.

    Cuadrado de cada número
     
    1
    2
    3
    4
    private static void Imprimir(this IEnumerable<int> arraInt)
    {
          Console.WriteLine($"[\"{String.Join("\", \"", arraInt.Select(x => x.ToString()))}\"]");
    }
     
     
    private static void Imprimir(this IEnumerable<int> arraInt)
    {
          Console.WriteLine($"[\"{String.Join("\", \"", arraInt.Select(x => x.ToString()))}\"]");
    }
    

    El código anterior significa que para todas las clases que implemente IEnumerable <int> (Arrays, Listas, colecciones y demás), le agrega un método para imprimir su contenido, de forma que podemos llamarlo así:

    cuadrados.Imprimir();

    En el caso del Select y la expresión lambda asociada (x=> x * x) vemos como se combinan de forma muy simple y funcional la programación orientada a objetos y la declarativa, haciendo que el código resultante, es sencillo y fácil de entender.

  • Consultas a colecciones de forma declarativas

    Es posible aplicar consultas a colecciones de objetos por ejemplo podemos tener el objeto Persona, y podemos realizar transformaciones y selecciones de forma muy simple y clara.

    Consultas a colecciones de objetos
     
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    // Creo una colección de personas.
    Persona[] personas = new Persona[]{
        new Persona { Nombre="Maria", Apellido="Jimenez"},
        new Persona { Nombre="Juan", Apellido="Perez"},
        new Persona { Nombre="Ana", Apellido="Moreno"},
        new Persona { Nombre="Ruben", Apellido="Gomez"},
        new Persona { Nombre="Pedro", Apellido="Sanchez"},
        new Persona { Nombre="Roberto", Apellido="Hernandez"}
    };
     
    // ********************************
    // Comprobar si un saludo elemento existe en un array
    // ********************************
     
    Console.WriteLine($"{personas.Any(x => x.Nombre == "Maria")}");
     
    // Salida: true
     
    // ********************************
    // Selecciono elementos no sean Maria o Juan
    // ********************************
     
    var sinMariaJose = personas
            .Where(x => x.Nombre != "Maria" && x.Nombre != "Jose")
            .Select(x => x);
     
    sinMariaJose.Imprimir();
     
    //Salida [ "Juan Perez", "Ana Moreno", "Ruben Gomez", "Pedro Sanchez", "Roberto Hernandez"]
     
    // *****************************************************
    // Devolver un array de elementos a mayusculas
    // *****************************************************
     
    var mayusculas = personas
        .Select(x =>
            new Persona
            {
                Nombre = x.Nombre.ToUpper(),
                Apellido = x.Apellido.ToUpper(),
            }
        );
     
    mayusculas.Imprimir();
     
    // Salida: ["HOLA JUAN", "HOLA PEDRO", "HOLA ELIAS", "HOLA GUSTAVO", "HOLA EDGAR"]
     
    //*****************************************************
    // Buscar un elemento que acaben en o
    // Busco aquellos mensajes que acaben con la letra o, aquí se usan expresiones lamba, las veremos después
    //*****************************************************
     
    var elementosAcabanO = personas
        .Where(x => Regex.IsMatch(x.Nombre, @"o$"))
        .Select(x => x);
     
    elementosAcabanO.Imprimir();
     
    // Salida: [ "Pedro Sanchez", "Roberto Hernandez"]
     
     
     // Creo una colección de personas.
    Persona[] personas = new Persona[]{
        new Persona { Nombre="Maria", Apellido="Jimenez"},
        new Persona { Nombre="Juan", Apellido="Perez"},
        new Persona { Nombre="Ana", Apellido="Moreno"},
        new Persona { Nombre="Ruben", Apellido="Gomez"},
        new Persona { Nombre="Pedro", Apellido="Sanchez"},
        new Persona { Nombre="Roberto", Apellido="Hernandez"}
    };
    
    // ********************************
    // Comprobar si un saludo elemento existe en un array
    // ********************************
    
    Console.WriteLine($"{personas.Any(x => x.Nombre == "Maria")}");
    
    // Salida: true
    
    // ********************************
    // Selecciono elementos no sean Maria o Juan
    // ********************************
    
    var sinMariaJose = personas
            .Where(x => x.Nombre != "Maria" && x.Nombre != "Jose")
            .Select(x => x);
    
    sinMariaJose.Imprimir();
    
    //Salida [ "Juan Perez", "Ana Moreno", "Ruben Gomez", "Pedro Sanchez", "Roberto Hernandez"]
    
    // *****************************************************
    // Devolver un array de elementos a mayusculas
    // *****************************************************
    
    var mayusculas = personas
        .Select(x =>
            new Persona
            {
                Nombre = x.Nombre.ToUpper(),
                Apellido = x.Apellido.ToUpper(),
            }
        );
    
    mayusculas.Imprimir();
    
    // Salida: ["HOLA JUAN", "HOLA PEDRO", "HOLA ELIAS", "HOLA GUSTAVO", "HOLA EDGAR"]
    
    //*****************************************************
    // Buscar un elemento que acaben en o
    // Busco aquellos mensajes que acaben con la letra o, aquí se usan expresiones lamba, las veremos después
    //*****************************************************
    
    var elementosAcabanO = personas
        .Where(x => Regex.IsMatch(x.Nombre, @"o$"))
        .Select(x => x);
    
    elementosAcabanO.Imprimir();
    
    // Salida: [ "Pedro Sanchez", "Roberto Hernandez"]
    

    C# tiene una sintaxis alternativa para las consultas parecida a SQL (aunque en lo personal no es mi forma favorita, simplifica la lectura):

    Consulta alternativa
     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // ********************************
    // Selecciono elementos no sean Maria o Juan
    // ********************************
     
    var sinMariaJoseAlternativo = from x in personas
                                    where x.Nombre != "Maria" && x.Nombre != "Jose"
                                    select x;
     
    sinMariaJoseAlternativo.Imprimir();
     
     
    // ********************************
    // Selecciono elementos no sean Maria o Juan
    // ********************************
    
    var sinMariaJoseAlternativo = from x in personas
                                    where x.Nombre != "Maria" && x.Nombre != "Jose"
                                    select x;
    
    sinMariaJoseAlternativo.Imprimir();
    

  • Consultas a colecciones de forma paralela

    Generalmente la programación paralela es complicada, hay muchos puntos a tener en cuenta, como sincronizaciones, esperas, candados, pero mediante C# y de forma declarativa, podemos recorrer un array de forma paralela, simplemente exponiéndolo con la opción “AsParallel()”.

    Consulta paralela
     
    1
    2
    3
    4
    5
    6
    7
    8
    9
    // ********************************
    // Selecciono elementos no sean Maria o Juan
    // ********************************
     
    var sinMariaJose = personas.AsParallel()
            .Where(x => x.Nombre != "Maria" && x.Nombre != "Jose")
            .Select(x => x);
     
    sinMariaJose.Imprimir();
     
     
    // ********************************
    // Selecciono elementos no sean Maria o Juan
    // ********************************
    
    var sinMariaJose = personas.AsParallel()
            .Where(x => x.Nombre != "Maria" && x.Nombre != "Jose")
            .Select(x => x);
    
    sinMariaJose.Imprimir();
    

    Salida: [ "Juan Perez", "Ana Moreno", "Ruben Gomez", "Pedro Sanchez", "Roberto Hernandez"]

    Simplemente poniendo AsParallel() a continuación del array se recorre de forma paralela, optimizando el proceso entre los varios núcleos de una CPU.

Las expresiones lambda pueden ser datos y código


Como vemos las expresiones lambda son ideales para expresar lo que queremos conseguir, en lugar de cómo obtenerlo.

Lambda nos permite unir la programación en C# y las consultas en SQL, mediante alguno ORM, por ejemplo Entity Framework.

Por ejemplo teniendo la base de datos Northwind, pudiéramos hacer una consulta a la tabla employe de la siguiente forma:

Consulta paralela
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using (var context = new NorthwindEntities())
{
    // Descomente la siguiente linea para mostar las sentencias SQL generadas
    //  context.Database.Log = Console.WriteLine;
 
    var consulta = from e in context.Employees
                    where e.FirstName.Contains("a")
                    select (e.TitleOfCourtesy + " " + e.FirstName + " " + e.LastName);
 
    // El codigo anterior equivale al siguiente
    // var consulta = context.Employees
    //    .Where(e => e.FirstName.Contains("a"))
    //    .Select(e => e.TitleOfCourtesy + " " + e.FirstName + " " + e.LastName);
 
    //SQL GENERADO
    // SELECT CASE
    //      WHEN ([Extent1].[TitleOfCourtesy] IS NULL)
    //          THEN N''
    //      ELSE [Extent1].[TitleOfCourtesy]
    //      END + N' ' + [Extent1].[FirstName] + N' ' + [Extent1].[LastName] AS [C1]
    // FROM [dbo].[Employees] AS [Extent1]
    // WHERE [Extent1].[FirstName] LIKE N'%a%'
 
    var resultado = consulta.ToArray();
 
    Console.WriteLine(String.Join(Environment.NewLine, resultado));
 
}
 
 
using (var context = new NorthwindEntities())
{
    // Descomente la siguiente linea para mostar las sentencias SQL generadas
    //  context.Database.Log = Console.WriteLine;

    var consulta = from e in context.Employees
                    where e.FirstName.Contains("a")
                    select (e.TitleOfCourtesy + " " + e.FirstName + " " + e.LastName);

    // El codigo anterior equivale al siguiente
    // var consulta = context.Employees
    //    .Where(e => e.FirstName.Contains("a"))
    //    .Select(e => e.TitleOfCourtesy + " " + e.FirstName + " " + e.LastName);

    //SQL GENERADO
    // SELECT CASE
    //      WHEN ([Extent1].[TitleOfCourtesy] IS NULL)
    //          THEN N''
    //      ELSE [Extent1].[TitleOfCourtesy]
    //      END + N' ' + [Extent1].[FirstName] + N' ' + [Extent1].[LastName] AS [C1]
    // FROM [dbo].[Employees] AS [Extent1]
    // WHERE [Extent1].[FirstName] LIKE N'%a%'

    var resultado = consulta.ToArray();

    Console.WriteLine(String.Join(Environment.NewLine, resultado));

}

Salida:

Ms.Nancy Davolio

Dr.Andrew Fuller

Ms.Janet Leverling

Mrs.Margaret Peacock

Mr.Michael Suyama

Ms.Laura Callahan

Ms.Anne Dodsworth


El código anterior realiza las siguientes acciones:


  1. Se conecta la base de datos Northwind

  2. Ejecuta un código de consulta SQL sobre la tabla empleados

  3. Recupera el nombre completo de todos los empleados

  4. Cierra la conexión.


Todo esto sin realizar los pesados pasos para conectarse a una base de datos, y ejecutar código SQL, todo se especifica de forma declarativa.

La parte declarativa puede expresarse de dos formas, y las dos son exactamente equivalentes.

Primera forma:

Consulta Linq
 
1
2
3
var consulta = from e in context.Employees
                where e.FirstName.Contains("a")
                select (e.TitleOfCourtesy + " " + e.FirstName + " " + e.LastName);
 
 
var consulta = from e in context.Employees
                where e.FirstName.Contains("a")
                select (e.TitleOfCourtesy + " " + e.FirstName + " " + e.LastName);

Segunda forma:

Consulta alternativa
 
1
2
3
var consulta = context.Employees
    .Where(e => e.FirstName.Contains("a"))
    .Select(e => e.TitleOfCourtesy + " " + e.FirstName + " " + e.LastName);
 
 
var consulta = context.Employees
    .Where(e => e.FirstName.Contains("a"))
    .Select(e => e.TitleOfCourtesy + " " + e.FirstName + " " + e.LastName);

Ahora bien, no es muy inteligente, traer una tabla completa en memoria (de la maquina cliente), y realizar un buscara allá, pero eso es lo que pensaríamos al ver la sentencia tal (sobre todo en la segunda forma, pero realmente hacen lo mismo). El caso es que la expresión lambda en este escenario no hace la función de ser código, si no datos, que serán convertidos en una sentencia SQL, de forma completa al momento de ejecutarse, es decir mediante código podemos expresar una consulta SQL de forma íntegra y declarativa.




Si te ha gustado la entrada, ¡Compártela! ;-)

Nota: Puedes encontrar todo el código fuente de este artículo en https://github.com/jbautistamartin/ParadigmasTiposLenguajes