Sistema D’Hont con C#

Sistema D’Hont con C#

En el post de Arithmos, explico el sistema de selección de diputados mediante el método D’Hont, pero como no, se me ocurrió crear el sistema bajo C# y ahí va.


    public class CongresoDiputados
    {
        public CongresoDiputados()
        {
            Circunscripciones = new List();
            PartidosCircunscripciones = new List();
            PartidosCongreso = new List();
        }
        public List Circunscripciones { get; set; }
        public List PartidosCircunscripciones { get; set; }
        public List PartidosCongreso { get; set; }

        public void Update(List circunscripciones)
        {
            Circunscripciones = circunscripciones;
            circunscripciones.ForEach(c => PartidosCircunscripciones.AddRange(c.PartidosCircunscripcion));
            PartidosCongreso = (from u in PartidosCircunscripciones
                         group u by u.Nombre into newGroup
                         select new PartidoPolitico(newGroup.Key)
                         {
                             DiputadosAsignados = newGroup.Sum(s => s.DiputadosAsignados),
                             NumVotos = newGroup.Sum(s=>s.NumVotos)
                         }).ToList();
        }

        public new string ToString => $"Congreso de los Diputados - Resultado Elecciones\n{PartidosCongreso.Aggregate("", (s, p) => s + p.ToString)}";
    }

    public class Circunscripcion
    {
        public Circunscripcion(string nombre, int diputados)
        {
            NombreCircunscripcion = nombre;
            Diputados = diputados;
            PartidosCircunscripcion = new List();
        }
        public string NombreCircunscripcion { get; set; }
        public int Diputados { get; set; }

        public List PartidosCircunscripcion { get; set; }

        public void Update(List partidos)
        {
            partidos.ForEach(p => Update(p));
        }

        public void Update(PartidoPolitico partido)
        {
            partido.Cociente = Diputados;
            if (PartidosCircunscripcion.Contains(partido))
            {
                PartidosCircunscripcion.Where(p => p.Nombre == partido.Nombre).FirstOrDefault().NumVotos = partido.NumVotos;
            }
            else
            {
                PartidosCircunscripcion.Add(partido);
            }

            var list = new List();
            PartidosCircunscripcion.ForEach(k => list.AddRange(k.Distribuciones));
            var listaOrdenada= list
                .OrderByDescending(v => v.ParticionVotos)
                .Take(Diputados);
            PartidosCircunscripcion.ForEach(k => k.DiputadosAsignados = listaOrdenada.Where(p => p.Nombre == k.Nombre).Count());

        }

        public new string ToString => $"{PartidosCircunscripcion.Aggregate("", (s, p) => s + p.ToString)}";
    }

    public class PartidoPolitico
    {
        public PartidoPolitico(string nombre) : this(nombre, 0) { }

        public PartidoPolitico(string nombre, int numVotos)
        {
            Nombre = nombre.PadRight(10,' ');
            NumVotos = numVotos;
        }

        public string Nombre { get; set; }
        public int NumVotos { get; set; }

        public int Cociente { get; set; } = 10;

        public int DiputadosAsignados { get; set; }

        public List Distribuciones
        {
            get {
                var l = new List();
                l.AddRange(Enumerable.Range(1, Cociente).ToList().Select(n => new Distribucion(Nombre, NumVotos / n)).ToList());
                return l;
            }
        }

        public new string ToString => $"{Nombre}\t{NumVotos} Votos\t\tDiputados:\t{DiputadosAsignados}\n";
    }

    public class Distribucion
    {
        public Distribucion(string nombre, int votos)
        {
            Nombre = nombre;
            ParticionVotos = votos;
        }
        public string Nombre { get; set; }
        public int ParticionVotos { get; set; }
    }

He incluido alguno de los resultados de las elecciones del 10N del 2019 y he aquí los resultados

    class Program
    {
        static void Main(string[] args)
        {

            Circunscripcion Malaga = new Circunscripcion("Málaga", 11);
            Malaga.Update(new List() {
                new PartidoPolitico("PSOE", 225344),
                new PartidoPolitico("PP", 162467),
                new PartidoPolitico("Vox", 161657),
                new PartidoPolitico("UP", 96988),
                new PartidoPolitico("Cs", 66524)});
            Console.WriteLine(Malaga.ToString);

            Circunscripcion Madrid = new Circunscripcion("Madrid", 37);
            Madrid.Update(new List() {
                new PartidoPolitico("PSOE", 948751),
                new PartidoPolitico("PP", 879667),
                new PartidoPolitico("Vox", 647924),
                new PartidoPolitico("UP", 459030),
                new PartidoPolitico("Cs", 316310),
                new PartidoPolitico("Mas Pais", 199172) });
            Console.WriteLine(Madrid.ToString);

            CongresoDiputados Congreso = new CongresoDiputados();
            Congreso.Update(new List()
            {
                Malaga, Madrid
            });
            Console.WriteLine(Congreso.ToString);
            Console.Read();
        }
    }

PSOE            225344 Votos            Diputados:      4
PP              162467 Votos            Diputados:      3
Vox             161657 Votos            Diputados:      2
UP              96988 Votos             Diputados:      1
Cs              66524 Votos             Diputados:      1

PSOE            948751 Votos            Diputados:      10
PP              879667 Votos            Diputados:      10
Vox             647924 Votos            Diputados:      7
UP              459030 Votos            Diputados:      5
Cs              316310 Votos            Diputados:      3
Mas Pais        199172 Votos            Diputados:      2

Congreso de los Diputados - Resultado Elecciones
PSOE            1174095 Votos           Diputados:      14
PP              1042134 Votos           Diputados:      13
Vox             809581 Votos            Diputados:      9
UP              556018 Votos            Diputados:      6
Cs              382834 Votos            Diputados:      4
Mas Pais        199172 Votos            Diputados:      2

Como siempre, el código mejorable.

Dead XOR Alive

Dead XOR Alive

En uno de mis blogs, en Arithmos, publiqué un post sobre una historia de carceleros y presos; minutos después de acabar el artículo, dije, ¿por qué no hago una app que lo muestre?, dicho y hecho, me puse y en una tarde desarrollada estaba.

Y ¿por qué digo esto? para demostrar lo fácil que es desarrollar una pequeña aplicación con C# y WPF.

Si leen el artículo, podrán observar que la aplicación solo muestra un tablero de ajedrez, con monedas en cada casilla con una cara o una cruz aleatoriamente, un cuadro mágico que es el elegido por el carcelero y un cuadro del tablero que debe calcular uno de los presos para que el siguiente convicto les salve de la muerte, este último cuadro no es otro que una operación XOR.

El modelo podría ser una clase Chessboard que alberga objetos del tipo CoinBox, las monedas que ya de camino le diremos que color tiene el cuadro donde se apoyan en el tablero.

    public class Chessboard
    {
        public List CoinsBox { get; set; }

        public Chessboard() : this(-1) { }

        public Chessboard(int headsCount)
        {
            CoinsBox = new List();
            FillChessboard(headsCount);
        }

        ///
        /// Rellena el tablero con cuadros y monedas aleaotorias con cara y cruz
        /// 

        /// Número de caras. si es -1, se genera aletoriamente
        void FillChessboard(int headsCount)
        {
            var random = new Random();

            headsCount = headsCount == -1 ? random.Next(64) : headsCount;

            // Llenar Lista con monedas con cruz
            Enumerable.Range(0, 64).ToList()
            .ForEach(n => CoinsBox.Add(new CoinBox(n, false, (n / 8) % 2 == 0 ? n % 2 == 0 : n % 2 != 0)));

            // Cambiar n monedas a cara
            Enumerable.Range(0, headsCount).ToList().ForEach(n => CoinsBox[random.Next(64)].IsHeads = true);

            // Cuadro mágico
            CoinsBox[random.Next(64)].IsMagicBox = true;

            // Cuadro que debe cambiar
            var changed= CoinsBox
                .Where(n => n.IsHeads).Aggregate(MagicBox.Index, (acum, n) => acum ^ n.Index, op => op);
            CoinsBox[changed].IsChangedBox = true;

        }

        public CoinBox MagicBox => CoinsBox.Where(n => n.IsMagicBox).FirstOrDefault();
        public CoinBox ChangedBox => CoinsBox.Where(n => n.IsChangedBox).FirstOrDefault();
        public int ParityChessboard => CoinsBox
                .Where(n => n.IsHeads).Aggregate(0, (acum, n) => acum ^ n.Index, op => op);

        public string ParityChessboardToString => $"La paridad del tablero es {ParityChessboard}";
        public string ResultToString => $"Si cambiamos la moneda {ChangedBox.Index}, la paridad del tablero será {MagicBox.Index} que es el cuadrado mágico!!!";
        public string MagicBoxToString => $"El cuadro mágico seleccionado por el carcelero es el número {MagicBox.Index}";
        public string ChangeBoxToString => $"La moneda que debe cambiar el convicto es la número {ChangedBox.Index}";
        public string ListaXORToString => $"La operación XOR de las monedas que tienen cara {string.Join(", ", CoinsBox.Where(k => k.IsHeads).Select(i => i.Index))} y el cuadro mágico {MagicBox.Index} da como resultado {ChangedBox.Index} o la operación XOR entre el cuadro mágico {MagicBox.Index} y la paridad del tablero {ParityChessboard} es {ChangedBox.Index}";
    }

    public class CoinBox
    {

        public CoinBox(int index, bool isHeads, bool isBlack)
        {
            Index = index;
            IsHeads = isHeads;
            IsBlack = isBlack;
        }

        public int Index { get; set; }

        public bool IsHeads { get; set; }

        public bool IsTails { get { return !IsHeads; } }

        public bool IsChangedBox { get; set; }

        public bool IsMagicBox { get; set; }

        public bool IsBlack { get; set; }
    }

Como algo particular, la asignación del color del cuadro mediante la operación (n / 8) % 2 == 0 ? n % 2 == 0 : n % 2 != 0) con la que con el índice del cuadro le decimos si el cuadro es negro o blanco o las expresiones lambda que calculan la operación XOR sobre una lista de elementos mediante la función lambda Aggregate, la cual le pasamos un parámetro acumulador inicial y posteriormente es usado elemento a elemento con la operación XOR.Para la vista, he creado un control definido por el usuario llamado Box, el cual contiene el Binding cambiando su aspecto según las propiedades del objeto CoinBox asociado a su Datacontext. Como podéis ver a continuación, creo converter para transformar valores booleanos en Visibility, Brush o Bitmap. (Si alguien los quiere los converter, los cuelgo)

<UserControl x:Class="DxorAlive.Box"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:DxorAlive"
             mc:Ignorable="d" 
             d:DesignHeight="43" d:DesignWidth="43" Margin="1">
    <UserControl.Resources>
        <local:BoolToVisibleConverter x:Key="b2tvc" Reverse="False"/>
        <local:ImageConverter x:Key="itc"/>
        <local:BoolToColorConverter x:Key="btcc"/>
    </UserControl.Resources>
    <Grid>

        <Rectangle Width="64" Height="64" Stroke="Gray" StrokeThickness="1" Fill="{Binding IsBlack, Converter={StaticResource btcc}}"/>
        <Rectangle Width="62" Height="62" Stroke="Red" StrokeThickness="2" Visibility="{Binding IsMagicBox, Converter={StaticResource b2tvc}}"/>
        <Rectangle Width="60" Height="60" Stroke="Orange" StrokeThickness="2" Visibility="{Binding IsChangedBox, Converter={StaticResource b2tvc}}"/>
        <Image Source="{Binding IsHeads, Converter={StaticResource itc}}" Height="48"/>
    </Grid>
</UserControl>

Ahora solo nos queda crear o con xml o desde la clase de la vista (runtime) una matriz de objetos cada uno con su datacontext. Yo lo he he desarrollado con código en vez de crear 64 cuadros del tipo Box. Llamamos al método FillChessboard y se genera un nuevo tablero.

        private void FillChessboard(int headsCount)
        {
            grid.Children.Clear();
            // Crear tablero
            var cb = new Chessboard(headsCount);

            //crear cuadros y monedas
            cb.CoinsBox.ForEach(n =>
            {
                var c = new Box();
                c.DataContext = n;
                var row = n.Index / 8;
                var col = n.Index % 8;
                Grid.SetRow(c, row);
                Grid.SetColumn(c, col+1);
                grid.Children.Add(c);
            });

            this.DataContext = cb;

        }

he aquí el resultado. Un saludo a tod@s

deadxoralive1.png
Os dejo el enlace del ejecutable

PD: Y como llevo una temporada enamorado de Kotlin, os paso código en este lenguaje para una app de consola. I ♥ kt

 
import kotlin.random.Random

fun main(args: Array) {
    val c= Chessboard(5)
    println(c)

}


class Chessboard(var headsCount: Int) {
    val coinsBox = mutableListOf()

    init {
        headsCount = if (headsCount == -1) Random.nextInt(64) else headsCount
        fillChessboard()
    }

    private fun fillChessboard() {
        // Rellenar de monedas
        (0..63).toList().forEach {
            coinsBox.add(CoinBox(it, false, if ((it / 8) % 2 == 0) (it % 2 == 0) else (it % 2 != 0)))
        }
        // Caras
        (0..headsCount!!).toList().forEach { coinsBox[Random.nextInt(64)].isHeads = true }
        // cuadro mágico
        val vMB = Random.nextInt(64)
        coinsBox[vMB].isMagicBox = true
        // moneda cambiante
        coinsBox[coinsBox.filter { i-> i.isHeads }.fold(vMB) { acc, opXOR -> acc.xor(opXOR.index) }].isChangedBox=true

    }

    override fun toString(): String {
        return "$magic\n$parityToString\n$changed\n$result\n$listaXOR\n$chessboard"
    }

    val magicBox = coinsBox.filter { n -> n.isMagicBox }.firstOrNull()
    val changedBox = coinsBox.filter { n -> n.isChangedBox }.firstOrNull()
    val parityChessBoard = coinsBox[coinsBox.filter { i-> i.isHeads }.fold(0) { acc, opXOR -> acc.xor(opXOR.index) }]

    val parityToString= "La paridad del tablero es ${parityChessBoard.index}"
    val result = "Si cambiamos la moneda ${changedBox?.index}, la paridad del tablero será ${magicBox?.index} que es el cuadrado mágico"
    val magic = "El cuadro seleccionado por el carcelero es el número ${magicBox?.index}"
    val changed = "La moneda que debe cambiar el convicto es la número ${changedBox?.index}"
    val listaXOR = "La operación XOR de las monedas que tienen cara ${coinsBox.filter { k-> k.isHeads }.joinToString(", "){it.index.toString()}} " +
            "y el cuadro mágico ${magicBox?.index} da como resultado ${changedBox?.index} " +
            "\no la operación XOR entre el cuadro mágico ${magicBox?.index} y la paridad del tablero ${parityChessBoard.index} es ${changedBox?.index}"
    val chessboard = coinsBox.joinToString("") {k-> "\t${k.index} ${if (k.isHeads) "H" else "T"} ${if ((k.index+1) % 8==0) "\n" else ""}"  }

}


data class CoinBox(
    val index: Int,
    var isHeads: Boolean,
    var isBlack: Boolean,
    var isChangedBox: Boolean = false,
    var isMagicBox: Boolean = false

Binding WPF vs Binding Android – I

Binding WPF vs Binding Android – I
Este post, es el inicio de una tetralogía con el que pretendo mostrar los aspectos de desarrollo de ambas plataformas, Microsoft y Android en cuanto al uso de la arquitectura MVVM y Databinding.
Antes de nada veamos cuales son las diferencias esenciales del modelo vista controlador (MVC) y el modelo vista vista-modelo (MVVM).

Colecciones I

Colecciones I

Hoy vamos a ver como aprovecharnos de herramientas que nos permiten usar un código más limpio y entendible en cuanto a colecciones se refiere y para ello veremos como hacerlo en este post para colecciones con expresiones lambda y Linq de .NET con C# y en el siguiente post, haremos esto mismo pero con Java.

En primer lugar crearemos un clase  Persona, dándole algunas propiedades como nombre, apellidos, nacimiento, un valor booleano para conocer el género, fecha de nacimiento y lugar de nacimiento, además le añadimos una propiedad de solo lectura que calcule la edad, toString, una para obtener el nombre completo y otro método booleano para saber si es nacido en un lugar concreto.

namespace ColeccionesDemo
{
    public class Persona
    {
        public Persona(string _name,
            string _surname,
            bool _isMan,
            DateTime _birthDate,
            string _birthPlace)
        {
            Name = _name;
            SurName = _surname;
            IsMan = _isMan;
            BirthDate = _birthDate;
            BirthPlace = _birthPlace;
        }

        public string Name { get; set; }
        public string SurName { get; set; }
        public DateTime BirthDate { get; set; }
        public string BirthPlace { get; set; }
        public bool IsMan { get; set; }
        public int Age
        {
            get
            {
                DateTime now = DateTime.Now;
                return now.Year - BirthDate.Year + (now.DayOfYear Nombre: {0}, Apellidos: {1}, Edad: {2}, De: {3}", Name, SurName, Age, BirthPlace); }
        }
        public string FullName
        {
            get { return string.Format("{0} {1}", Name, SurName); }
        }
        public override bool Equals(object obj)
        {
            var persona = obj as Persona;
            return persona != null &&
                   Name == persona.Name &&
                   SurName == persona.SurName;
        }
    }
}

Una vez creada la clase persona, para C# vamos a crear un objeto que almacene esta clase con una colección List la cual implementa la interfaz IEnumerable y a la cual le añadiremos algunos individuos ficticios.

      static List personas = new List();

        static void addPersonas()
        {
            personas.Add(new Persona("Joaquin", "Martinez Rus", true, new DateTime(1969, 2, 20), "Linares"));
            personas.Add(new Persona("Vicky", "Diez Calatayud", false, new DateTime(1996, 4, 19), "Calatayud"));
            personas.Add(new Persona("Ana Isabel", "Martinez Baloo", false, new DateTime(1998, 10, 20), "Cordoba"));
            personas.Add(new Persona("Victoria", "Martinez Nurse", false, new DateTime(1992, 1, 21), "Cordoba"));
            personas.Add(new Persona("Ana", "Rus Maria", false, new DateTime(1961, 11, 3), "Linares"));
            personas.Add(new Persona("Manuel", "Bonillo Contador", true, new DateTime(1978, 12, 26), "Cordoba"));
            personas.Add(new Persona("Jose Antonio", "Martinez Arcos", true, new DateTime(1974, 9, 4), "Zaragoza"));
            personas.Add(new Persona("Vicente", "Rodriguez Iglesias", true, new DateTime(1981, 11, 8), "Cordoba"));
            personas.Add(new Persona("Alfonso", "Perez Judicial", true, new DateTime(2000, 5, 5), "Linares"));
            personas.Add(new Persona("Ana", "Martinez Maestre", false, new DateTime(2004, 3, 4), "Linares"));
            personas.Add(new Persona("Magdalena", "Fuentes Cruz", false, new DateTime(2002, 7, 11), "Jaen"));
            personas.Add(new Persona("Jose", "Martinez Cintas", true, new DateTime(1981, 6, 21), "Linares"));
            personas.Add(new Persona("Jose", "Aldea Morata", true, new DateTime(1994, 11, 22), "Calatayud"));
            personas.Add(new Persona("Maria Isabel", "Diez Campieles", false, new DateTime(2005, 8, 1), "Calatayud"));
            personas.Add(new Persona("Maria", "Bella Adriá", false, new DateTime(2001, 10, 22), "Jaen"));
            personas.Add(new Persona("Pedro José", "Garcia Civil", true, new DateTime(1980, 9, 15), "Cordoba"));
            personas.Add(new Persona("Luis", "Jiloca Diez", true, new DateTime(1995, 6, 15), "Zaragoza"));
            personas.Add(new Persona("Juan", "Pintor Escultor", true, new DateTime(2000, 10, 22), "Jaen"));
            personas.Add(new Persona("Juan", "Goran Esteban", true, new DateTime(1974, 7, 14), "Jaen"));
            personas.Add(new Persona("Andrea", "Jiloca Diez", false, new DateTime(1999, 9, 21), "Calatayud"));
            personas.Add(new Persona("Daniel", "Ceular Flower", true, new DateTime(1975, 2, 4), "Cordoba"));<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>
        }

Una vez añadidos, vamos a pedirle a nuestra colección de personas unas cuantos datos con criterios concretos.
Ejemplo 1. ¿cual es el promedio de edad de todas las personas nacidas en Calatayud?
Vamos a crear tres métodos, uno paso a paso, calcAverageAge(string _birthPlace), es decir inicializamos variables, iteramos por la colección, comprobamos la condición, calculamos e imprimimos. El segundo método mediante programación funcional calcAverageAgeFunctionalProgramming(string _birthPlace), escribiremos una línea para calcular el promedio y otra para imprimirlo (aunque lo podía haber hecho en la misma línea), por último otro modo sería con Linq igual de vistoso que el anterior con el método calcAverageAgeLinq(string _birthPlace)

static void calcAverageAge(string _birthPlace)
        {
            double suma = 0;
            double count = 0;
            double average = 0;
            foreach (var persona in personas)
            {
                if (persona.IsFrom(_birthPlace))
                {
                    suma += persona.Age;
                    count++;
                }
            }
            average = suma / count;
            Console.WriteLine(string.Format("Las personas nacidas en {0} tienen un promedio de edad de {1} ", _birthPlace, average));

        }

        static void calcAverageAgeFunctionalProgramming(string _birthPlace)
        {
            var average= personas.Where(n => n.IsFrom(_birthPlace)).Average(p => p.Age);
            Console.WriteLine(string.Format("Programación funcional\nLas personas nacidas en {0} tienen un promedio de edad de {1} ", _birthPlace, average));
        }

        static void calcAverageAgeLinq(string _birthPlace)
        {
            var average = (from u in personas
                         where u.IsFrom(_birthPlace)
                         select u).Average(p => p.Age);
            Console.WriteLine(string.Format("Programación funcional\nLas personas nacidas en {0} tienen un promedio de edad de {1} ", _birthPlace, average));
        }

Ejemplo 2. Queremos listar todos los mayores de edad por orden alfabético de los apellidos.
Con el método estándar printAdults() y como queremos ordenar por los apellidos hemos tenido que incluir la interfaz IComparable que implementa el método CompareTo(Persona other) efectuando la comparación de los apellidos y ejecutando el método Sort de la clase List. En el siguiente método con programación funcional printAdultsFunctionalProgramming(), en una sola línea efectuamos todas las operaciones sin hacer referencia a ninguna interfaz IComparable y por último el mismo método basado en Linq printAdultsLinq(). Este tipo de expresiones en una línea, las llamamos expresiones lambda.

static void printAdults()
        {
            personas.Sort();
            foreach (var persona in personas)
            {
                if (persona.Age>17)
                {
                    Console.WriteLine(persona.FullName);
                }
            }
        }

        static void printAdultsFunctionalProgramming()
        {
            Console.WriteLine("******************************************");
            Console.WriteLine("Mayores de edad con programación funcional");
            Console.WriteLine("******************************************");
            personas.Where(p => p.Age > 17).
                OrderBy(p=>p.SurName).
                ToList().
                ForEach(p => Console.WriteLine(p.FullName));
        }

        static void printAdultsLinq()
        {
            Console.WriteLine("************************");
            Console.WriteLine("Mayores de edad con Linq");
            Console.WriteLine("************************");
            (from persona in personas
                      where persona.Age > 17
                      orderby persona.SurName
                      select persona).ToList().ForEach(p => Console.WriteLine(p.FullName));
        }

Resultado, el mismo, pero a mi entender, claridad en el código máxima con programación funcional. En el siguiente post, haremos lo mismo pero con Java.
coleccion1coleccion2

Documentar el código

Documentar el código

El código además de ser limpio, legible, entendible o estructurado entre otras características, debe estar muy bien documentado con el fin de efectuar un mantenimiento, modificación o ampliación de nuestra fuente. Además, es posible generar documentación en base a estos comentarios como es el caso de JavaDoc.

Existen muchas formas de comentar el código, por ejemplo:

C#, Java, PHP, etc.

  • Solo C#. Podemos hacerlo con tres barras /// en la que al crear una nueva línea, la siguiente línea queda también comentada con ///. Si las escribimos antes de una clase o un método, genera etiquetas de comentario que expondré a continuación.
  • Dos barras, crea una línea de código comentado
  • Comentario multilínea. Una barra y un asterisco /* provoca que el código esté comentado hasta que se cierre el comentario con asterisco barra */

Visual basic.NET

  • Comillas simples para comentar línea. ‘Esto es un comentario en VB.NET
  • Usar la etiqueta REM. REM Esto es un comentario en VB.NET
  • Si queremos crear varias líneas, debemos usar el concatenador guión bajo
    REM Esto es un comentario_ 
    en VB.NET

HTML

  • para cerrar el comentario

Comentarios de clases y métodos

Cuando estamos escribiendo código e instanciamos una clase o seleccionamos un método de una clase, los IDE,s nos muestran una ayuda que nos muestra que hace el método, que parámetros tiene o que sobrecargas y esto se consigue del siguiente modo:

C#

    • Escribimos /// precediendo a una clase o un método y se generan automáticamente una serie de etiquetas xml. La documentación de las etiquetas la pueden encontrar en el siguiente enlace.
      /// <summary>
      /// Obtiene el nombre completo del objeto Persona
      /// </summary>
      /// <param name="dni">DNI del objeto persona</param>
      /// <returns>Cadena de texto con el nombre completo</returns>
      public string GetFullName(string dni)
      {
      	return "Joaquín Martínez;
      }
      
      /// <summary>
      /// Obtiene el nombre completo del objeto Persona
      /// </summary>
      /// <param name="id">Identificador del objeto persona</param>
      /// <returns>Cadena de texto con el nombre completo</returns>
      public string GetFullName(int id)
      {
      	return "Joaquín Martínez;
      }
      

Java

    • Escribimos encima de un método /** e INTRO y se genera las etiquetas para documentar el código
      /**
      * Obtiene el nombre completo del objeto Persona
      * @param dni DNI del objeto persona
      * @return Cadena de texto con el nombre completo
      */
      public String getFullName(String dni){
      	return "Joaquín Martínez";
      }
      
      /**
      * Obtiene el nombre completo del objeto Persona
      * @param id Identificador del objeto persona
      * @return Cadena de texto con el nombre completo
      */
      public String getFullName(int id){
      	return "Joaquín Martínez";
      }
      

      coment04coment05

Regiones

Otra forma de mantener el código ordenado es mediante regiones, esto nos va a permitir por ejemplo mantener en una clase de negocio los métodos de actualización, adición y eliminación separados de los de selección, mantener propiedades en una región, campos en otra, métodos públicos en otra, privados en otra y así cuantas regiones queramos tener con el fin de mantener el código ordenado. Yo personalmente en una clase, generalizando, siempre tengo una región para constructores y destructores, una para enumeraciones, una para campos, una para métodos públicos y protected, otra para privados y si la clase es un form o window, añado una para controles y dentro de esta, por tipo de control, añado métodos de evento de cada uno, por ejemplo, Buttons, DataGrid, etc.

C#

#region Métodos públicos
        // aquí podemos poner todos los
        // miembros de clase que queramos
#endregion

VB.NET

#Region "Métodos Públicos"
' aquí podemos poner todos los
' miembros de clase que queramos
#End Region

def45-7-2-1
Java

// <editor-fold desc="Métodos públicos" defaultstate="collapsed">
        // aquí podemos poner todos los
        // miembros de clase que queramos
// </editor-fold>

coment06.png

Tareas

Visual Studio tiene comentarios de tareas, es decir si incluimos una serie de comentarios específicos (TOKEN) en alguna parte del código con un comentario adicional, en la lista de tareas, podemos acceder a esta localización de código para continuar con la tarea pendiente. Estos Token pueden personalizarse en el Menú Herramientas, Opciones, Lista de tareas. En este ejemplo, he colocado un TOKEN para revisar el contenido del método y en la lista de tareas, al hacer doble click, nos dirigiremos al código en cuestión.

coment07.png

Hay que añadir y es una buena costumbre, documentar las clases con autoría, fechas de creación y modificación, que hace la clase, que función tiene, las modificaciones que se han realizado, etc.

Hacedme caso, un poco tiempo perdido en documentar, nos hará ganar muchísimo tiempo en un futuro.

coment08.png

 

Distancia de Levenshtein

Distancia de Levenshtein

La distancia de Levenshtein es el número mínimo que necesitamos para convertir una palabra en otra, un ejemplo pelo y perro tienen tienen una distancia de 2 porque tendríamos que sustituir la l por una r y añadir una r, dos movimientos, distancia 2. Otro ejemplo, murcielago y muerdago que tienen una distancia de 5, ya que a murciélago le tenemos que quitar c,i,e,l y a muerdago le quitamos la d, por tanto necesitamos 5 movimientos para convertir una palabra en otra. Resumiendo, cuanto más pequeña sea la distancia, más parecidas son las dos palabras.

¿Esto para que sirve?, pues muy fácil, para obtener palabras similares en los buscadores o mostrar palabras alternativas por ejemplo. Vamos a ver el algoritmo y como funciona.

Este algoritmo es una matriz de n filas por m columnas donde n y m son el número de caracteres + 1 de cada palabra. El primer paso que se realiza es rellenar la primera fila y la primera columna con números secuenciales de 0 hasta n+1 y desde 0 hasta m+1 , quedando del siguiente modo para el caso del ejemplo inicial la matriz de 11×9:

levensthein0Una vez tenemos la matriz inicial, empezaremos a realizar comparaciones por filas de cada caracter con el caracter de cada columna. En cada operación, obtenemos primero el peso y tres valores de los cuales deduciremos el menor de ellos como valor final y con un ejemplo lo veremos mejor.

Comenzamos comparando los caracteres, la letra “m” de murcielago (en la columna), lo comparamos con la “m” de muerdago (filas), si es el mismo caracter se le asigna un peso de 0 y si es diferente, le asignamos 1, en este caso como son iguales, le asignamos un 0. A continuación obtenemos los tres valores, el primero con la casilla superior sumándole al valor de esta un 1, en este caso 1+1=2, el siguiente con la casilla que está situada a su izquierda haciéndolo de igual modo, 1+1=2 y por último a la casilla superior izquierda, la que está situada en la diagonal a la que le sumaremos el valor del peso, 0+0=0; de estos tres valores, cogeremos el menor que en este caso es el 0. levensthein1

Vamos a por el siguiente, peso igual a 0, porque u es igual a u, por la izquierda, 0+1=1, la diagonal 1+0=1 y la superior 1+2=2, el mínimo de los tres es 1

levensthein2

continuando con las comparaciones y obtención del mínimo, quedaría nuestra matriz completa del siguiente modo:

levensthein3

El último valor, es la distancia de Levenshtein, en este caso es 5, si quisieramos ver la proporción de acierto, deberíamos obtener la la diferencia con la unidad de la relación entre la distancia y el número de caracteres del mayor en este caso, 1 – (5/10)=0,5 o 50%. La distancia de “murcielago” y “cerro muriano” es de 10, por tanto 1-(10/13)=0.2308 o del 23,08%.

El algoritmo implementado con lenguajes de programación no tiene ningún misterio para el que esté acostumbrado a las matrices, además en Wikipedia, podéis encontrar su implementación en diferentes lenguajes, por ejemplo en php ya tiene una función como tal.

vladimir-levenshtein¿Que utilidades podemos darle? por ejemplo palabras sugerentes en base a un diccionario o aproximaciones, corrección de ortografía, etc. y además, existen variantes del algoritmo como la de Damerau-Levenshtein, otros más complejos, algoritmos fonéticos.

Este algoritmo se lo debemos a Vladimir Levenshtein, un matemático ruso que lo desarrolló en 1965.

He hecho en C# una app de consola como muestra para obtener la matriz y en base a un diccionario, nos muestre sugerencia de palabra.  levensthein4

levensthein5

Semáforo II

Semáforo II
En esta entrega vamos a crear el semáforo. Aunque no lo parezca, pero un semáforo a pesar de su simplicidad visual, tiene aspectos bastante complejos y sobre todo cuando tienen que estar sincronizados o comunicados con otros, existen muchas matemáticas detrás de los modelos de tráfico de las grandes ciudades.

Sin darle más complejidad que la que necesitamos para nuestro proyecto final, que es un sistema de control de semáforos en una avenida para poder cruzarla de una sola vez sin pararnos, le asignaremos las siguientes características:

  • Tiempo en verde
  • Tiempo en amarillo
  • Tiempos en rojo
  • Tiempo de retardo para sincronziación de semáforos
  • Estado en el que se encuentra
  • Función de funcionamiento en manual o automático
  • Función de inicio automático
  • Función de parpadeo

En el diseño XAML, creamos unos objetos rectangle y le incluimos los controles de usuario creados en el post Semáforo I. El cambio de color lo podiamos haber hecho con converter, con propiedades, pero esta vez he optado por hacerlo con estilos y DataTrigger. A cada objeto LEDLight (luz del semáforo) le asigno un estilo genérico con algún efecto de fundido para que parezca más real; luego este estilo es heredado por otros tres estilos, uno por cada color donde los DataTrigger hacen su trabajo comprobando el estado del semáforo y activando la propiedad LightIsEnabled a Truesi cumple con la condición del DataTrigger. (Código al final del post)

Luego en el código de clase, le añado un DispatcherTime que nos hará las funciones de temporizador, creamos unas cuantas propiedades de dependencia para cumplir con nuestras características.
Tiene un método privado que será llamado desde el temporizador y este asignará el estado al semáforo y como consecuencia activando el color correspondiente.

        void changingState()
        {
            // Retardo inicial
            if (DelayTime > 0)
            {
                timer.Interval = new TimeSpan(0, 0, (int)DelayTime);
                DelayTime = 0;
                return;
            }

            // Parpadeando
            if (IsFlashingAmberLight)
            {
                if (State.Equals(LEDTrafficLightState.Yellow))
                {
                    State = LEDTrafficLightState.None;
                    timer.Interval = new TimeSpan(0, 0, 0, 0, 500);
                }
                else
                {
                    State = LEDTrafficLightState.Yellow;
                    timer.Interval = new TimeSpan(0, 0, 0, 1);
                }

                return;
            }

            // Cambio de color
            switch (State)
            {
                case LEDTrafficLightState.Red:
                    // SEMÁFORO EN VERDE
                    State = LEDTrafficLightState.Green;
                    
                    timer.Interval = new TimeSpan(0, 0, GreenTime);
                    break;
                case LEDTrafficLightState.Yellow:
                    // SEMÁFORO EN VERDE
                    State = LEDTrafficLightState.Red;
                    timer.Interval = new TimeSpan(0, 0, RedTime);
                    break;
                case LEDTrafficLightState.Green:
                    // SEMÁFORO EN AMBAR
                    State = LEDTrafficLightState.Yellow;
                    timer.Interval = new TimeSpan(0, 0, YellowTime);
                    break;
                default:
                    break;
            }
        }

y este el resultado instanciando varios semáforos en una ventana

            <controls:LEDTrafficLight x:Name="LEDTL1" Width="100" Height="300" GreenTime="4" RedTime="4" DelayTime="1" Margin="10"/>
            <controls:LEDTrafficLight x:Name="LEDTL2" Width="100" Height="300" GreenTime="5" RedTime="4" DelayTime="2" Margin="10"/>
            <controls:LEDTrafficLight x:Name="LEDTL3" Width="100" Height="300" GreenTime="5" RedTime="5" DelayTime="3" Margin="10"/>
            <controls:LEDTrafficLight x:Name="LEDTL4" Width="100" Height="300" GreenTime="5" RedTime="5" IsFlashingAmberLight="True" Margin="10"/>  

Video7
La próxima entrega, el sistema de control de semáforos

<UserControl x:Class="Controls.LEDTrafficLight"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:controls="clr-namespace:Controls"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="100">
    <Grid x:Name="grid">
        <Grid.Resources>
            <Style x:Key="LightStyle" TargetType="controls:LEDLight">
                <Setter Property="Margin" Value="5"/>
                <Style.Triggers>
                    <Trigger Property="LightIsEnabled" Value="True">
                        <Trigger.ExitActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation
                  Storyboard.TargetProperty="Opacity"
                  From="0" To="1" Duration="0:0:0.2" />
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.ExitActions>
                        <Trigger.EnterActions>
                            <BeginStoryboard>
                                <Storyboard>
                                    <DoubleAnimation
                  Storyboard.TargetProperty="Opacity"
                  From="0" To="1" Duration="0:0:0.1" />
                                </Storyboard>
                            </BeginStoryboard>
                        </Trigger.EnterActions>
                    </Trigger>
                </Style.Triggers>
            </Style>
            <Style x:Key="RedLightStyle" TargetType="controls:LEDLight" BasedOn="{StaticResource LightStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=controls:LEDTrafficLight},Path=State }" Value="{x:Static controls:LEDTrafficLight+LEDTrafficLightState.Red}">
                        <Setter Property="LightIsEnabled" Value="True"/>
                    </DataTrigger>

                </Style.Triggers>
            </Style>
            <Style x:Key="YellowLightStyle" TargetType="controls:LEDLight" BasedOn="{StaticResource LightStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=controls:LEDTrafficLight},Path=State }" Value="{x:Static controls:LEDTrafficLight+LEDTrafficLightState.Yellow}">
                        <Setter Property="LightIsEnabled" Value="True"/>
                    </DataTrigger>

                </Style.Triggers>
            </Style>
            <Style x:Key="GreenLightStyle" TargetType="controls:LEDLight" BasedOn="{StaticResource LightStyle}">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=controls:LEDTrafficLight},Path=State }" Value="{x:Static controls:LEDTrafficLight+LEDTrafficLightState.Green}">
                        <Setter Property="LightIsEnabled" Value="True"/>
                    </DataTrigger>

                </Style.Triggers>
            </Style>
            <Style x:Key="rectangleTraficLight" TargetType="Border">
                <Setter Property="Width" Value="{Binding ElementName=redEllipse, Path=ActualWidth}"/>
                <Setter Property="Height" Value="5"/>
                <Setter Property="Background" Value="Gray"/>
                <Setter Property="Margin" Value="1"/>
                <Setter Property="VerticalAlignment" Value="Top"/>
                <Setter Property="CornerRadius" Value="3"/>
            </Style>
            <Style x:Key="borderColor" TargetType="Border">
                <Setter Property="BorderBrush" Value="Gray"/>
                <Setter Property="BorderThickness" Value="2"/>
                <Setter Property="Margin" Value="2,0,2,2"/>
                <Setter Property="CornerRadius" Value="3"/>
            </Style>
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="100*"/>
            <RowDefinition Height="100*"/>
            <RowDefinition Height="100*"/>
        </Grid.RowDefinitions>
        <Border Grid.RowSpan="3" BorderBrush="DarkGreen" BorderThickness="5" Background="DarkGreen" Opacity=".5" CornerRadius="5"/>
        <Border Style="{StaticResource borderColor}" Grid.Row="0">
            <controls:LEDLight x:Name="RedLight" LEDLightBackgroundON="Red" Style="{StaticResource RedLightStyle}"/>
        </Border>
        <Border Style="{StaticResource borderColor}" Grid.Row="1">
            <controls:LEDLight x:Name="YellowLight"  LEDLightBackgroundON="Gold" Style="{StaticResource YellowLightStyle}"/>
        </Border>
        <Border Style="{StaticResource borderColor}" Grid.Row="2">
            <controls:LEDLight x:Name="GreenLight" Style="{StaticResource GreenLightStyle}"/>
        </Border>
    </Grid>
</UserControl>
namespace Controls
{
    /// <summary>
    /// Lógica de interacción para LEDTrafficLight.xaml
    /// </summary>
    public partial class LEDTrafficLight : UserControl
    {
        #region Constructor

        public LEDTrafficLight()
        {
            InitializeComponent();
            this.DataContext = this;
            timer.Tick += Timer_Tick;
            timer.IsEnabled = StartAuto;
        }

        #endregion

        #region Enum

        public enum LEDTrafficLightState
        {
            Red,
            Yellow,
            Green,
            None
        }

        #endregion

        #region DependencyProperties

        public int GreenTime
        {
            get { return (int)GetValue(GreenTimeProperty); }
            set
            {
                value = value > YellowTime ? value - YellowTime : YellowTime + 1;
                SetValue(GreenTimeProperty, value);
            }
        }

        public int RedTime
        {
            get { return (int)GetValue(RedTimeProperty); }
            set
            {
                if (value <= 0) value = 1;
                SetValue(RedTimeProperty, value);
            }
        }

        public int YellowTime
        {
            get { return (int)GetValue(YellowTimeProperty); }
            set { SetValue(YellowTimeProperty, value); }
        }

        public int DelayTime
        {
            get { return (int)GetValue(TimeProperty); }
            set { SetValue(TimeProperty, value); }
        }

        public bool IsFlashingAmberLight
        {
            get { return (bool)GetValue(IsFlickingProperty); }
            set
            {
                SetValue(IsFlickingProperty, value);
                State = LEDTrafficLightState.Yellow;
                // en 100 ms cambia a Parpadeo
                timer.Interval = new TimeSpan(0, 0, 0, 0, 100);
            }
        }

        public bool IsManual
        {
            get { return (bool)GetValue(IsManualProperty); }
            set
            {
                SetValue(IsManualProperty, value);
                timer.IsEnabled = !value;
            }
        }

        public bool StartAuto
        {
            get { return (bool)GetValue(StarAutoProperty); }
            set
            {
                SetValue(StarAutoProperty, value);
                timer.IsEnabled = value;
            }
        }

        public LEDTrafficLightState State
        {
            get { return (LEDTrafficLightState)GetValue(StateProperty); }
            set { SetValue(StateProperty, value); }
        }

        #region DependecyProperties Register

        // Using a DependencyProperty as the backing store for StarAuto.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StarAutoProperty =
            DependencyProperty.Register("StartAuto", typeof(bool), typeof(LEDTrafficLight), new PropertyMetadata(true));

        // Using a DependencyProperty as the backing store for IsManual.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsManualProperty =
            DependencyProperty.Register("IsManual", typeof(bool), typeof(LEDTrafficLight), new PropertyMetadata(false));

        // Using a DependencyProperty as the backing store for IsFlicking.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsFlickingProperty =
            DependencyProperty.Register("IsFlashingAmberLight", typeof(bool), typeof(LEDTrafficLight), new PropertyMetadata(false));

        // Using a DependencyProperty as the backing store for State.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StateProperty =
            DependencyProperty.Register("State", typeof(LEDTrafficLightState), typeof(LEDTrafficLight), new PropertyMetadata(LEDTrafficLightState.Red));

        // Using a DependencyProperty as the backing store for Time.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty TimeProperty =
            DependencyProperty.Register("DelayTime", typeof(int), typeof(LEDTrafficLight), new PropertyMetadata(0));

        // Using a DependencyProperty as the backing store for TimeYellow.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty YellowTimeProperty =
            DependencyProperty.Register("YellowTime", typeof(int), typeof(LEDTrafficLight), new PropertyMetadata(2));

        // Using a DependencyProperty as the backing store for TimeRed.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty RedTimeProperty =
            DependencyProperty.Register("RedTime", typeof(int), typeof(LEDTrafficLight), new PropertyMetadata(7));

        // Using a DependencyProperty as the backing store for TimeGreen.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GreenTimeProperty =
            DependencyProperty.Register("GreenTime", typeof(int), typeof(LEDTrafficLight), new PropertyMetadata(7));

        #endregion

        #endregion

        #region Fields & Const & Event

        DispatcherTimer timer = new DispatcherTimer();

        #endregion


        #region Publics Method

        /// <summary>
        /// Inicia el semáforo cuando se encuentra en modo automático
        /// </summary>
        public void Start()
        {
            if (IsManual) return;
            timer.Start();
        }

        /// <summary>
        /// Detiene el semáforo
        /// </summary>
        public void Stop()
        {
            timer.Stop();
        }

        /// <summary>
        /// Cambia el semáforo a rojo
        /// </summary>
        public void OnRed()
        {
            State = LEDTrafficLightState.Red;
        }

        /// <summary>
        /// Cambia el semáforo a verde
        /// </summary>
        public void OnGreen()
        {
            State = LEDTrafficLightState.Green;
        }

        /// <summary>
        /// Cambia el semáforo a amarillo
        /// </summary>
        public void OnYellow()
        {
            State = LEDTrafficLightState.Yellow;
        }

        public void Test()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Privates methods

        /// <summary>
        /// Cambia el estado del semáforo según el estado anterior
        /// </summary>
        void changingState()
        {
            // Retardo inicial
            if (DelayTime > 0)
            {
                timer.Interval = new TimeSpan(0, 0, (int)DelayTime);
                DelayTime = 0;
                return;
            }

            // Parpadeando
            if (IsFlashingAmberLight)
            {
                if (State.Equals(LEDTrafficLightState.Yellow))
                {
                    State = LEDTrafficLightState.None;
                    timer.Interval = new TimeSpan(0, 0, 0, 0, 500);
                }
                else
                {
                    State = LEDTrafficLightState.Yellow;
                    timer.Interval = new TimeSpan(0, 0, 0, 1);
                }

                return;
            }

            // Cambio de color
            switch (State)
            {
                case LEDTrafficLightState.Red:
                    // SEMÁFORO EN VERDE
                    State = LEDTrafficLightState.Green;
                    
                    timer.Interval = new TimeSpan(0, 0, GreenTime);
                    break;
                case LEDTrafficLightState.Yellow:
                    // SEMÁFORO EN VERDE
                    State = LEDTrafficLightState.Red;
                    timer.Interval = new TimeSpan(0, 0, RedTime);
                    break;
                case LEDTrafficLightState.Green:
                    // SEMÁFORO EN AMBAR
                    State = LEDTrafficLightState.Yellow;
                    timer.Interval = new TimeSpan(0, 0, YellowTime);
                    break;
                default:
                    break;
            }

        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            changingState();
        }

        #endregion
    }
}