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

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


Si pusiéramos ordenados en una línea los lenguajes de programación, estarían en un punto los más cercanos a cómo funciona una computadora, que nos hacen estar al pendiente de temas tal como la memoria, el rendimiento, o los periféricos, y en el otro punto de la línea, tendremos lenguajes llamados declarativos. En estos lenguajes se especifica que es lo que deseamos obtener y no como obtenerlo.



No hay que confundir la programación declarativa con usar el lenguaje natural para resolver un problema, si no con dejar de pensar en cómo lo resolvería la computadora y comenzar a pensar en cómo lo resolveríamos nosotros (como personas). Algunos tipos de lenguajes declarativos se parecen al lenguaje natural (en la forma de expresar necesidades), pero la mayoría se parecen a declaraciones matemáticas (en las que también pensamos y estructuramos nuestras ideas)

Programación funcional




En la programación funcional, toda resolución de una necesidad es el resultado de una función (que devuelve un valor),   es decir todo se tiene que resolver mediante el uso de funciones, (y no se secuencias de operaciones, como en la programación declarativa).

Aquí vemos el primer enfoque matemático,   asociando el resultado de una operación   al resultado de una función.

No existen bucles como tal, sino que todo tipo de iteración, debe ser el resultado de una recursividad. Tampoco existen variables, como tal.

Por otro lado una función, siempre devuelve el mismo resultado si se le proporciona los mismos parámetros de entrada, a diferencia de por ejemplo, la programación orientada a objetos, en la que el resultado de una función depende del estado del objeto (de sus atributos).

El principal representante de la programación funcional es LISP, y aquí hay una curiosidad, por el orden que hemos seguido, pareciera que los lenguajes declarativos están al final de la historia de los lenguajes de programación. Pues bien LISP es anterior a todos los lenguajes de programación de alto nivel, solo fortran es más viejo que él. LISP fue creado en el año 58.

¿Por qué el pronto surgimiento, de LISP?, posiblemente por facilidad de expresar pensamientos matemáticos, y la naturaleza científica/matemática que tenían de origen los primeros científicos de la computación.

Entonces, ¿Por qué se impusieron, los otros tipos de lenguajes, como los imperativos?, posiblemente por la dificultad de implementar un compilador LISP para diferentes plataformas y arquitecturas, y la dificultad de crear sistemas de aplicación (como por ejemplo las usadas en un banco o para gestionar información). Es decir en ese momento, y cuando se vio un tema comercial en las computadoras, parecía más sencillo la creación de lenguajes imperativos, que de lenguajes de corte más científico. Aquí vemos como la ingeniera (resolución practica de problemas), se impuso a la ciencia (teoría de cómo se deben resolver problemas).

Vamos a ver un ejemplo de cómo resolver un problema de forma imperativa   y de forma funcional.

En este caso resolveremos un factorial. El factorial de un entero es la multiplicación de todos los números que son memores que él, por ejemplo el factorial de cinco, se calcularía así:

5! = 1 x 2 x 3 x 4 x 5= 120

Si quisiéramos resolver este problema en C, seria de la siguiente forma:

?
Código Imperativo
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
#include <stdio.h>
  
int main ()
    int n=5, fact=1;
    for (int c = n; c >= 1; c--) {
     
        fact = fact * c;
    }
    
    printf ("Factorial= %d\n", fact);
    
    return 0;
}
#include <stdio.h> 
 
int main () 
{  
    int n=5, fact=1;

    for (int c = n; c >= 1; c--) {
    
        fact = fact * c;
    }
   
    printf ("Factorial= %d\n", fact);
   
    return 0;

}

Si quisiéramos darle otro enfoque pudiéramos, establecer las siguientes reglas.


  • El factorial de uno es uno (el de cero también): factorial(1)=1
  • El factorial de cualquier otro número (positivo y entero) es igual a el mismo número, multiplicado por el factorial del numero que lo precede: factorial(N)=N*factorial(N-1)

Para resolver el problema del factorial en LISP, procedemos de la siguiente forma:

; Declaro una funcion para calcular el factorial
(defun factorial (n)
  (if (= n 0) 
      1 ;Si n es cero el resultado es 1
      (* n (factorial (- n 1))) ) ) ; En cualquier otro lugar el resultado es N * factorial del numero anterior


(print (factorial 5))

En LISP todo es una función, por ejemplo si quiero realizar una resta, la función es el signo menos y los parámetros el numero original y el numero que quiero restar, por ejemplo para restar uno a n (n-1), tengo que ponerlo de la siguiente forma (- n 1).

Nótese que se expresa la necesidad, de una forma más clara y directa que en un lenguaje imperativo, estamos en sí, expresando que queremos y no como obtenerlo.

Evidentemente, es posible solucionar el tema usando recursión de una forma muy parecida en C, pero originalmente la recursividad era mal vista en los primeros lenguajes debido a que agregaban un costo extra a la ejecución de los programas, tanto es así que lenguajes imperativos como fortran no la implementaban de origen, obligando a buscar una solución interactiva a todo problema. Hoy en día, la recursividad da claridad a los problemas y hace sistemas fácilmente comprensibles, debe haber una justificación muy buena, para no buscar una solución recursiva a un problema que lo permita.

Programación lógica


La programación lógica es otra forma de resolver problemas expresando lo que queremos y no como resolverlo. En si se expresan una serie de condiciones que debe cumplir la solución, y el lenguaje (motor) encuentra una solución al problema.



El representante clásico sobre la programación lógica es PROLOG, creado en los años 70, veamos algunos ejemplos en PROLOG.

Como comentábamos matemáticamente hablando, el factorial se define de esta forma


  • factorial(0) = 1
  • factorial(1) = 1
  • factorial(N) = N*Factorial(N-1)

El código el PROLOG seria:

% La sintaxis es factorial(N, F) -> Factorial de N es F (el resultado se guarda en F)
factorial(0, 1). % El Factorial de 0 es 1, esto es un HECHO
factorial(1, 1). % El Factorial de 1 es 1, esto es un HECHO
factorial(N, F) :-  % El factorial de F de N
 N>0,      % N tiene que ser mayor que 0, esto es una REGLA
 N1 is N - 1,  % N1 es el entero anterio
 factorial(N1, F1), % F1 es Factorial de N1
 F is N * F1. % F es el numero N * el factorial anterior.


/** 

¿Cuál es el Factorial de uno?
? - factorial(1,F).
F = 1

¿Cuál es el Factorial de cinco?
? - factorial(5,F).
F = 5

¿Es 120 factoroal de 5?
? - factorial(5,120).
yes

¿Es 121 factorual de 5?
? - factorial(5,121)
no

*/


  • ¿Cuál es el Factorial de uno?

    ? - factorial( 1,F)

    La respuesta es F = 1

  • ¿Cuál es el Factorial de cinco?

    ? - factorial( 5,F)

    La respuesta es F = 120


Ahora viene lo interesante. Puedo preguntarle si un número es factorial de otro, y me debe indicar si es cierto o falso, por ejemplo


  • ¿Es 120 factorial de 5?

    ? - factorial( 5,120)

    La respuesta es true (verdadero)

  • ¿Es 121 factorial de 5?

    ? - factorial( 5,121)

    La respuesta es false (falso)


Veamos otro ejemplo algo más elaborador, supongamos que tenemos este árbol familiar.


Podemos generar en PROLOG los siguientes hechos:


%%
%% declaraciones
%%
padrede('Juan', 'María'). % Juan es padre de María
padrede('Pablo', 'Juan'). % Pablo es padre de Juan
padrede('Pablo', 'Marcela'). % Pablo es padre de Marcela
padrede('Carlos', 'Débora'). % Carlos es padre de Débora


Y las siguientes reglas:

% A es hijo de B si B es padre de A
hijode(A,B) :-  padrede(B,A).
% A es abuelo de B si A es padre de C y C es padre B
abuelode(A,B) :-    padrede(A,C), padrede(C,B).
% A y B son hermanos si el padre de A es también el padre de B y si A y B no son lo mismo
hermanode(A,B) :-    padrede(C,A), padrede(C,B), A \== B.        

% A y B son familiares si A es padre de B o A es hijo de B o A es hermano de B
familiarde(A,B) :-   padrede(A,B).
familiarde(A,B) :-   hijode(A,B). 
familiarde(A,B) :-   hermanode(A,B).
familiarde(A,B) :-   abuelode(A,B).

Podemos realizar las siguientes preguntas al motor:


  • ¿Juan es hermano de Marcela?

    ? - hermanode ('Juan', 'Marcela').

    La respuesta es true (verdadero)

  • ¿Carlos es hermano de Juan?

    ? - hermanode ('Carlos', 'Juan').

    La respuesta es false (falso)

  • ¿Pablo es abuelo de María?

    ? - abuelode ('Pablo', 'María').

    La respuesta es true (verdadero).

  • ¿María es abuela de Pablo?

    ? - abuelode ('María', 'Pablo').

    La respuesta es true (verdadero).

  • ¿Pablo y Carlos son familiares?

    ? - familiarde ('Pablo', 'Carlos').

    La respuesta es false (falso)

  • ¿Pablo y Maria son familiares?

    ? - familiarde ('Pablo', 'María').

    La respuesta es false (falso)


Estos dos lenguajes, LISP y PROLOG, han sido influencia en muchos lenguajes de programación. Sus ideas y enfoque se han aplicado a lenguajes imperativos y orientados a objetos, debido a lo sencillo y potente que son para expresar ideas. En breve veremos algunos ejemplos de su influencia en el desarrollo de los lenguajes de programación.

Lenguajes específicos del dominio


Los lenguajes específicos del domino ( Domain Specific Languages , DSL) son aquellos que están diseñados para resolver un problema en concreto. Lo contrario a los DSL, son los lenguajes de propósito general (es decir aquellos que no tiene un propósito en particular). Generalmente los DSL suelen ser lenguajes declarativos, debido que al enfocarse en la resolución de una necesidad en concreto intentan eliminar cualquier tipo de conocimiento sobre la arquitectura de la maquina y son más cercano el lenguaje propio de la necesidad (del dominio de la aplicación).



Algunos ejemplos de lenguaje declarativos

Structure Query Lenguaje (SQL)


SQL es uno de los lenguajes más importantes a nivel empresarial, y es un gran ejemplo de programación declarativa y de DSL.



Es un DSL por que tiene un objetivo en concreto, que es realizar consultas sobre bases de datos, no sirve para ningún otro tipo de tarea.

¿Por qué es declarativo?, es declarativo por que especificamos (quizás en este lenguaje de forma más clara que en otros), lo que deseamos obtener, y no tenemos ningún conocimiento acerca de cómo se va a obtener.

Profundicemos más en lo anterior. SQL es estándar, que no sabe desde que plataforma se está consumiendo (puede ser Java, C++, C#, o cualquier lenguaje que se pueda conectar a una base de datos), y no sabe a qué base de datos está realizándose la consulta (puede ser MySQL , SqlServer , Oracle…), lo único que contempla el lenguaje es la posibilidad de indicar que queremos obtener, el cómo hacerlo será trabajo del motor de la base de datos.

Veamos un ejemplo en la base de datos Northwind (base de datos ejemplo de Microsoft, que se simula los datos de una empresa):

-- Ventas por empleado
SELECT Employees.EmployeeID
 ,Employees.FirstName
 ,Employees.LastName
 ,count(Orders.OrderId) NumeroVentas
FROM Employees
INNER JOIN Orders ON Employees.EmployeeID = Orders.EmployeeID
GROUP BY Employees.EmployeeID
 ,Employees.FirstName
 ,Employees.LastName

--Obtener los clientes que no han realizado una compra desde 1997
SELECT CustomerID
 ,CompanyName
FROM Customers
WHERE NOT EXISTS (
  SELECT *
  FROM orders
  WHERE Customers.CustomerID = ORders.CustomerID
   AND OrderDate > '1997-12-31'
  )

--Obtener los jefes intermedios, estos son jefes que tiene personal a su cargo, en el ultimo nivel jeraquico 
-- (la gente que depende de el, no tiene a su vez a gente que dependa de ellos)
SELECT *
FROM Employees JefesIntermedios
WHERE EXISTS (
  SELECT *
  FROM Employees Dependientes
  WHERE Dependientes.EmployeeID = JefesIntermedios.ReportsTo
   AND NOT EXISTS (
    SELECT *
    FROM Employees DependientesUltimoNivel
    WHERE DependientesUltimoNivel.EmployeeID = Dependientes.ReportsTo
    )
  )

En ningún caso sabemos cómo se almacena este información, ni como la va a recuperar la base de datos, solo indicamos que es lo que queremos conseguir.

Expresiones Regulares


Las expresiones regulares son un DSL, creado para analizar cadenas de textos, si bien, en un principio parece un lenguaje complicado, una vez que se domina es casi imposible prescindir de ellos. Es un lenguaje funcionar, por que expresamos lo que queremos obtener de las cadenas de texto, y no la forma de obtenerlo.



Vamos a ver un ejemplo de expresiones regulares.

En México, cada contribuyente tiene un identificador asignado conocido como RFC (Registro federal de contribuyentes). A través de este número cada trabajador paga sus impuestos, un ejemplo, de RC, es el siguiente:

BETC 70 12 01 U76

Los campos de los componen un RFC, son los siguientes:

  • Cuatro letras extraídas de los nombres y apellidos del contribuyente: BETC
  • Año de nacimiento del contribuyente: 70
  • Mes de nacimiento del contribuyente 12
  • Día de nacimiento de contribuyente: 01
  • Tres dígitos verificadores, compuestos de letras y números: U76

Una expresión que validara que un RFC fuera correcto (en estructura), sería el siguiente:

^([A-ZÑ&]{4})([0-9]{2})(0[1-9]|1[0-2])(0[1-9]|1[0-9]|2[0-9]|3[0-1])([A-Z\d]{3})$


  • /^ :Debe ser el comienzo de la palabra
  • ([A-ZÑ&]{4}) : Debe contener cuatro caracteres, de la A a la Z, o Ñ o &.
  • ([0-9]{2}) : Dos numero del cero al nueve (año).
  • (0[1-9] | 1[0-2]) : Un cero seguido de un numero, o un uno seguido de un cero, uno o dos (mes).
  • (0[1-9]|1[0-9]|2[0-9]|3[0-1]) : Combinaciones posibles para la fecha del mes.
  • ([A-Z\d]{3}) : Cualquier combinación de números y letras que midan tres.
  • $: Fin de palabra.

HyperText Markup Language ( HTML )




HTML5 , junto a CSS3 y JavaScript, son parte de muchas de las aplicaciones que se usan en la actualidad. Podemos tener del lado del servidor multitud de lenguajes y herramientas, pero del lado del cliente solo tendremos esta tres trabajando en conjunto.

Es común encontrar bromas acerca del uso de HTML5, pero lo cierto es que es un DSL declarativo, fundamental hoy en un día.

Es un DSL, porque tiene una misión específica, que es estructurar el contenido de un sitio web (el CSS3, le dará apariencia, y el JavaScript funcionalidad).

Es declarativo, porque no indica en ningún lado, ni forma, ni el cómo se convertirá el contenido en los gráficos y texto que vera el usuario. Es el trabajo de cada navegador el renderizar el HTML, para convertirlo en algo visual para el usuario.

Un ejemplo pudiera ser el siguiente:

Nota: el ejemplo fue extraído de https://codepen.io/Dentz/pen/wMYRXY

?
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
<DOCTYPE! html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Basic html layout example</title>
</head>
<body class="hello">
  <header>
    <h1>< header ></h1>
  </header>
  <nav>
    < nav >
    <ul>
      <li></li>
      <li></li>
      <li></li>
    </ul>
  </nav>
  <section>
    < section >
    <header>< header ></header>
    <article>< article ></article>
    <footer>< footer ></footer>
  </section>
  <aside>
    < aside >
  </aside>
  <footer>
    < footer >
  </footer>
</body>
</html>

Si mostramos el documento HTML anterior, veremos algo no muy espectacular como


Pero en el HTML ya hemos definido los elementos estructurales de nuestra página, como la cabecera, un partes de secciones, barras de navegaciones, etc., Todos estos componentes pueden ser además analizados fácilmente por un buscador y indexador (puesto, que tienen una identidad fácilmente compresible.

Con un poco de CSS, nuestra página puede verse así:


Una de las cosas que me gustan de HTML, CSS y JS, es como ha sabido evolucionar para tener una identidad claramente diferenciada.   HTML se encarga de la estructura, CSS de la apariencia, y JS de la funcionalidad, todo está perfectamente separado, y todo tiene un lugar, con lo que se cumple la separación de responsabilidades (la ‘S’ de SOLID). Esto es particularmente hermoso, porque HTML nació como un lenguaje que tenía todo incrustado en su interior, apariencia, contenido y estructura, y que era realmente difícil de mantener, ahora es un modelo de como se debieran organizar las cosas, para que sean escalables.




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

Paradigmas y tipos de lenguajes informáticos (3 de 3). Orientación a Objetos


En el paradigma orientado a objetos ya nos acercamos más a como organizamos el pensamiento las personas. Nosotros para comprender el mundo real no nos fijamos muchos en los detalles, si no que abstraemos y clasificamos lo que nos rodea para poder comprenderlo, a cada “ser” le atribuimos propiedades y en base a que características de dichas propiedades les conceden, sabemos cómo interactuar con ellos.

Un ejemplo de esto son, los coches, poca gente sabe (en profundidad) cómo funciona el motor de un coche, o su sistema eléctrico, pero mucha gente sabe como operar un coche, incluso no tendría problemas para cambiar de modelo de coche y poder conducirlo inmediatamente, a pesar de que existe centenares de modelos de coches; automáticos, estándares, y de características muy variadas (incluso sin consideramos detalles estéticos como el color). El hecho de que una vez que hemos aprendiendo a conducir un coche, podamos sin mucho problemas, conducir varios modelos de coche, no implica que sepamos cómo funciona un coche realmente, si no que sabemos que es lo que estos pueden hacer, y en base a esto, podemos utilizarlo.

La POO, consiste en clasificar nuestros problemas, en “entidades”, que tienen características (llamadas atributos), y comportamientos conocidos, con los que podemos interactuar, a la vez que tienen características y comportamientos ocultos que no son necesarios conocer para poder interactuar con los anteriores.

El cómo se relacionen a través de comportamientos y características todas estas entidades es lo que conocemos como programación orientada a objetos.

Las entidades con sus características definidas y establecidas se llaman objetos, y los objetos de un mismo tipo son clasificadas en clases, por ejemplo si mi coche es un Nissan Versa Blanco del 2017, ese sería considerado el objeto, el modelo Nissan Versa seria la clase, con las características de color y año.

Una clase posee



  • Atributos: Representa el estado de la clase (una vez convertida en objeto), es lo que le da “personalidad” a la clase (en el ejemplo del coche seria el año o el color). Para acceder a los atributos, según el lenguaje, pueden usarse métodos o propiedades.
  • Mensajes: Las objetos pueden definir mensajes, que son instrucciones para realizar acciones, esto está representado por métodos (funciones y procedimiento), que pueden recibir cero o más parámetros.
  • Relaciones: las clases tienen relaciones de diversos tipos con otras clases:
    • Asociación: Dos clases (o más) están asociadas de alguna forma, puede ser que una clase sea un conjunto de otra clase (como una lista), o que sus atributos estén compuestos de otras clases.
    • Dependencia: Una clase usa a la otra.
    • Generalización/Especialización: Aquí entran todos los mecanismos de herencia

Características de la programación orientada a objetos



  • Encapsulamiento/Ocultación: La programación modular, ya tenía lago de encapsulamiento y ocultación.

    El encapsulamiento consístete en juntar en un mismo lugar todo lo que define a un objeto, esto es los datos (atributos), y la funcionalidad (métodos), de forma indivisible (uno de los grandes errores de la programación es considerar que los datos, deben estar aislados del negocio, esto es incorrecto, en la POO, los datos y el negocio, son una entidad unificada, un objeto, no tiene sentido, si no tiene datos y si no tiene negocio), todo esto hace referencia a la cohesión de una clase.

    El ocultamiento a su vez, significa que la clase debe mostrar lo menos posible sobre sí misma. Su funcionamiento y estructuras internas, no le interesa al resto de las clases, cuando menos expongamos más mantenible va a ser la clase, esto refuerza el bajo acoplamiento.
  • Herencia: es una de las principales relaciones entre clases, permite extender una clase creando otra que “herede” todas sus características y comportamientos, pero permitiéndola agregar nueva funcionalidad. La clase de la cual hereda se llama “clase” madre (o base) y la otra clase “hija”. En el caso del coche, la clase “coche”, seria la clase madre,  una clase con ciertas características comunes a todos los coches, pero la marca y el modelo seria la clase hija, “Nissan Versa”. Es posible que luego se hagan más coches que hereden de la marca, como particularidades o agregados por ejemplo podemos tener un “Nissa Versa Sense” que herede del anterior. El coche como tal, el que conducimos, es el objeto de clase, la “instancia” en sí.
  • Polimorfismo: El polimorfismo en la programación siempre hace referencia a que “algo” pueda tener distintas “caras”, según el contexto y la necesidad. En el POO, significa que se puedan usar las clases madre, sin preocuparnos la instancia de la clase hija que realmente es, por ejemplo podemos tener una función que envía “coches” al extranjero, sin preocuparnos si es un Nissan, un volvo, o un Renault, porque es irrelevante, lo que importa es que son coches (Aquí lo relevante serían sus dimensiones)

Ejemplo en C# de programación orientada a objetos.


Vamos a hacer un ejemplo con una colección de clases basadas en polígonos (cuadrados, rectángulos, etc.).Veamos el diagrama:



Polígono es una clase abstracta que representa una figura geométrica, que tiene lados, y como también un área, el cómo se calcula esta área depende del tipo de figura en sí, vemos que esta es una clase “incompleta”, sabe que es lo que tiene que hacer (Calcular un área), pero no sabe cómo hacerlo, de allí lo abstracto de la clase.

Clase Abstracta Polígono
 
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
/// <summary>
/// Representa un poligo, un poligono es una idea abstracta, hasta que no tiene lados y angulos, no es realmente un poligono.
/// </summary>
internal abstract class Poligono
{
    /// <summary>
    /// Numero de lados del poligono.
    /// </summary>
    public int Lados
    {
        get;
        private set;
    }
 
    /// <summary>
    /// Constructor del poligono.
    /// </summary>
    /// <param name="lados">Numero de lados</param>
    public Poligono(int lados)
    {
        this.Lados = lados;
    }
 
    /// <summary>
    /// Calcula la area, es una funcion abstracta, segun la figura la area se calcula de forma diferente.
    /// </summary>
    /// <returns>Area.</returns>
    public abstract double CalcularArea();
}
 
 
/// <summary>
/// Representa un poligo, un poligono es una idea abstracta, hasta que no tiene lados y angulos, no es realmente un poligono.
/// </summary>
internal abstract class Poligono
{
    /// <summary>
    /// Numero de lados del poligono.
    /// </summary>
    public int Lados
    {
        get;
        private set;
    }

    /// <summary>
    /// Constructor del poligono.
    /// </summary>
    /// <param name="lados">Numero de lados</param>
    public Poligono(int lados)
    {
        this.Lados = lados;
    }

    /// <summary>
    /// Calcula la area, es una funcion abstracta, segun la figura la area se calcula de forma diferente.
    /// </summary>
    /// <returns>Area.</returns>
    public abstract double CalcularArea();
}

Clase Rectángulo: Un rectángulo es propiamente un polígono, que ya tiene entidad, ya es algo concreto, la clase como tal tiene ancho y largo, y una forma específica de calcular su área.

Clase Rectángulo
 
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
/// <summary>
/// Representa un rectangulo, es un poligono que tiene ancho y largo.
/// </summary>
internal class Rectangulo : Poligono
{
    /// <summary>
    /// Ancho.
    /// </summary>
    public int Ancho
    {
        get;
        private set;
    }
 
    /// <summary>
    /// Largo.
    /// </summary>
    public int Largo
    {
        get;
        private set;
    }
 
    /// <summary>
    /// Constructor del rectangulo.
    /// </summary>
    /// <param name="ancho">Ancho.</param>
    /// <param name="largo">Largo.</param>
    public Rectangulo(int ancho, int largo) : base(4)
    {
        this.Ancho = ancho;
        this.Largo = largo;
    }
 
    /// <summary>
    /// Calcula el area del rectangulo.
    /// </summary>
    /// <returns>Area</returns>
    public override double CalcularArea()
    {
        return Ancho * Largo;
    }
}
 
 
/// <summary>
/// Representa un rectangulo, es un poligono que tiene ancho y largo.
/// </summary>
internal class Rectangulo : Poligono
{
    /// <summary>
    /// Ancho.
    /// </summary>
    public int Ancho
    {
        get;
        private set;
    }

    /// <summary>
    /// Largo.
    /// </summary>
    public int Largo
    {
        get;
        private set;
    }

    /// <summary>
    /// Constructor del rectangulo.
    /// </summary>
    /// <param name="ancho">Ancho.</param>
    /// <param name="largo">Largo.</param>
    public Rectangulo(int ancho, int largo) : base(4)
    {
        this.Ancho = ancho;
        this.Largo = largo;
    }

    /// <summary>
    /// Calcula el area del rectangulo.
    /// </summary>
    /// <returns>Area</returns>
    public override double CalcularArea()
    {
        return Ancho * Largo;
    }
}

Clase cuadrado: El cuadrado es a su vez un tipo de rectángulo en el que los lados miden lo mismo, aquí vemos una relación de herencia en la que un cuadrado es una particularización de un rectángulo y a su vez un rectángulo es un tipo de polígono.

Clase Cuadrado
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/// <summary>
/// Representa un cuadrado, un cuadrado es un rectangulo que tiene sus cuadro lados iguales.
/// </summary>
internal class Cuadrado : Rectangulo
{
    /// <summary>
    /// Tamaño del lado (todos los lados son iguales).
    /// </summary>
    public int TamanoLado
    {
        get
        {
            return Largo;
        }
    }
 
    /// <summary>
    /// Constructor de cuadrado.
    /// </summary>
    /// <param name="tamanoLado">tamaño de los lados.</param>
    public Cuadrado(int tamanoLado) : base(tamanoLado, tamanoLado)
    {
    }
}
 
 
/// <summary>
/// Representa un cuadrado, un cuadrado es un rectangulo que tiene sus cuadro lados iguales.
/// </summary>
internal class Cuadrado : Rectangulo
{
    /// <summary>
    /// Tamaño del lado (todos los lados son iguales).
    /// </summary>
    public int TamanoLado
    {
        get
        {
            return Largo;
        }
    }

    /// <summary>
    /// Constructor de cuadrado.
    /// </summary>
    /// <param name="tamanoLado">tamaño de los lados.</param>
    public Cuadrado(int tamanoLado) : base(tamanoLado, tamanoLado)
    {
    }
}

Veamos el siguiente código donde usamos todos los elementos en conjunto

En el siguiente método se suman el área de varios polígonos y se devuelve como resultado, notase que puede ser un polígono de cualquier forma, a la función eso le da igual, solo llama a la función de CalcularArea que es diferente en cada tipo de polígono (eso se llama polimorfismo, a pesar de que nuestra función trabaja con la clase base, la abstracta, se ejecuta cada uno de los métodos de CalcularArea de cada polígono).

 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
/// <summary>
/// El metodo suma las areas de un conjunto de poligonos, es de notar que no se especifica el tipo de poligono.
/// </summary>
/// <param name="poligonos"></param>
/// <returns></returns>
private static double SumarAreas(params Poligono[] poligonos)
{
    double result = 0;
    foreach (Poligono poligono in poligonos)
    {
        //Ejecuto por cada poligo su función calcular area, que es diferente segun el caso.
        result += poligono.CalcularArea();
    }
    return result;
}
 
 
/// <summary>
/// El metodo suma las areas de un conjunto de poligonos, es de notar que no se especifica el tipo de poligono.
/// </summary>
/// <param name="poligonos"></param>
/// <returns></returns>
private static double SumarAreas(params Poligono[] poligonos)
{
    double result = 0;
    foreach (Poligono poligono in poligonos)
    {
        //Ejecuto por cada poligo su función calcular area, que es diferente segun el caso.
        result += poligono.CalcularArea();
    }
    return result;
}

Veamos cómo se consume toda esta funcionalidad

 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
//Creo los poligonos, es de notar que cada poligono es de un tipo diferente.
 
Poligono rectangulo = new Rectangulo(2, 5);
Poligono cuadrado = new Cuadrado(8);
Poligono triangulo = new Triangulo(10, 12);
Poligono pentagono = new PentagonoRegular(2);
 
// Muestro el area de cada poligono
 
Console.WriteLine("El area de rectangulo es: " + rectangulo.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del cuadrado es: " + cuadrado.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del triangulo es: " + triangulo.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del pentagono es: " + pentagono.CalcularArea().ToString("0.##"));
 
// Muestro el area tota,
Console.WriteLine("El area total es: " + SumarAreas(rectangulo, cuadrado, triangulo, pentagono).ToString("0.##"));
Console.ReadLine();
 
 
//Creo los poligonos, es de notar que cada poligono es de un tipo diferente.

Poligono rectangulo = new Rectangulo(2, 5);
Poligono cuadrado = new Cuadrado(8);
Poligono triangulo = new Triangulo(10, 12);
Poligono pentagono = new PentagonoRegular(2);

// Muestro el area de cada poligono

Console.WriteLine("El area de rectangulo es: " + rectangulo.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del cuadrado es: " + cuadrado.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del triangulo es: " + triangulo.CalcularArea().ToString("0.##"));
Console.WriteLine("El area del pentagono es: " + pentagono.CalcularArea().ToString("0.##"));

// Muestro el area tota,
Console.WriteLine("El area total es: " + SumarAreas(rectangulo, cuadrado, triangulo, pentagono).ToString("0.##"));
Console.ReadLine();

Como vemos se crean los polígonos, indicando el tipo, pero asociándolo a una variable de tipo “polígono”, usando características polimórficas, vamos ejecutando cada uno de los métodos CalularArea, hasta que llamamos a SumarAreas, donde le pasamos el listado de figuras geométricas sin ningún orden en particular, puesto que esta función solo recibe polígonos.

¿Qué fue antes la clase o el objeto?


Una broma que encontré en internet, emulando a la famosa frase “que fue antes el huevo o la a pesar gallina”, es “¿Qué fue antes la clase o el objeto?”. A pesar que es un meme, se me hizo practico contestar la pregunta

Lo primero fue el objeto, esto es porque había datos y comportamiento antes de los que los clasificáramos, esto es además, el proceso de análisis normal, primero identificamos los elementos que queremos programar en un ejemplo práctico y después lo abstraemos para generalizarlo. Entonces podemos decir que la clase es realmente, una abstracción del objeto.




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

viernes, 13 de septiembre de 2019

¡Feliz día del programador 2019!

Cada año, el día 256, se festeja la mejor profesión, la de programador, así que:

 ¡Felicidades a todos los programadores, que día a día, trabajan para construir el futuro!