Xwork's Blog

The lord is waiting to take your hand.

Mostrando las entradas con la etiqueta depuracion. Mostrar todas las entradas
Mostrando las entradas con la etiqueta depuracion. Mostrar todas las entradas

Vamos a programar #38 - La conjetura de Collatz (Números maravillosos) versión C#.

Hola de nuevo a todos, el día de hoy vamos a hacer un pequeño receso a la programación en Pascal y vamos a ver un poco de C#.

La conjetura de Collatz dice que todos aquellos números son "maravillosos", siempre y cuando al aplicar las siguientes operaciones de forma recursiva, nos resulte 1.

El enunciado dice:

Sea la siguiente operación, aplicable a cualquier número entero positivo:
  • Si el número es par, se divide entre 2.
  • Si el número es impar, se multiplica por 3 y se suma 1.
El problema en si, es fácil, pero al momento de crear la aplicación, debemos de recordar que estamos limitados en el numero máximo que podemos usar. Para solucionarlo, vamos a usar el tipo "BigInteger".
En .net 4.0 se introdujo este nuevo tipo de dato, que permite utilizar enteros de longitud arbitraria, mas que suficiente para poder calcular la conjetura. Actualmente el número mas grande calculado tiene mas de 60 mil millones de cifras.

El código.

El codigo en c# que calcula la conjetura de Colltaz es el siguiente (la lista de controles no se incluye, todo el código fuente lo puedes descargar de dropbox):

using System;
using System.Windows.Forms;
using System.Numerics;
using System.Threading;

namespace ConjeturaDeCollatz
{
	public partial class FrmMain : Form
	{
		private delegate void UpdateListDelegate(string Value);

		private void UpdateList(string Value)
		{
			if (LBResults.InvokeRequired)
			{
				LBResults.Invoke(new UpdateListDelegate(UpdateList), Value);
			}
			else
			{
				LBResults.Items.Add(Value);
			}
		}
		private void CalculateCollatz(string Number)
		{
			BigInteger MyNumber;
			BigInteger.TryParse(Number, out MyNumber);
			while (MyNumber > 1)
			{
				if (MyNumber.IsEven == false)
				{
					MyNumber = MyNumber * 3 + 1;
					UpdateList(MyNumber.ToString());
				}
				else
				{
					MyNumber = MyNumber / 2;
					UpdateList(MyNumber.ToString());
				}
			}
		}
		private void RunWork(string Number)
		{
			Thread DoWork = new Thread(new ThreadStart(() => CalculateCollatz(Number)));
			DoWork.Start();
		}
		public FrmMain()
		{
			InitializeComponent();
		}

		private void BtnStart_Click(object sender, EventArgs e)
		{
			LBResults.Items.Clear();
			RunWork(TxtNumberIn.Text);

		}
	}
}


El código es realmente simple, ya que solo hacemos, para empezar, un bucle "while", que se ejecutará mientras la variable "MyNumber", sea mayor que uno. Después comprobamos si el numero es par, dentro de la estructura del "BigInteger", hay una función llamada "IsEven", la cual sirve para comprobar si un número es par. La utilizamos y si el resultado es "true", hacemos la división entre dos (cómo dice el enunciado), en caso contrario multiplicamos por 3 y le sumamos uno.
Para poder agilizar un poco, todo el proceso, se hace en un "thread" diferente, con lo cual la aplicación no se bloquea.
Por ahora, es todo. Desde mi punto de vista, parece que todos los numero son maravillosos, puesto que al ser impar (que usualmente son números raros), al multiplicar y sumarle uno, en algún punto, siempre nos dará un numero par. Aunque la conjetura no se ha probado (de ahí lo de conjetura), resulta un buen ejercicio para todo programador.
En el siguiente post de C#, agregaremos funciones para obtener más datos que son relevantes para la conjetura, Además, veremos un poco mas a fondo el tipo "BigInteger" y extenderemos el uso de threads para hacer cálculos un poco más "masivos". Cómo de costumbre, el código completo lo dejo en mi dropbox para que lo descargues.

Los leo luego.

Vamos a programar #35 - DecToAny.

Hola de nuevo a todos, el día de hoy vamos a ver código en C# que se encargará de convertir de un número decimal a cualquier base (en realidad hasta base 20). Al igual que en las ocasiones anteriores, gran parte del código, será una adaptación de lo que hemos hecho en post anteriores.

Haciendo un poco de memoria.

Para empezar recordemos que las conversión de un número decimal a un número en otra base, se realiza mediante divisiones sucesivas de el numero resultante de la división anterior entre la base. Si tenemos el numero X decimal y lo queremos pasar a la base B, debemos de dividir X/B y al resultado R, en la siguiente división lo dividiremos entre la base (B), este proceso se repite mientras que el resultado R sea mayor que la base.

Portando el código a c#.

Sabiendo lo anterior, podemos crear una funcion cómo la que sigue:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace DecToAny
{
 public partial class FrmMain : Form
 {
  private List<TextBox> TextBoxes = new List<TextBox>();
  private List<int> DecToAny(int Number, int Base)
  {
   int LastNumber = Number;
   int Garbage;
   List<int> result = new List<int>();
   if (LastNumber < Base)
   {
    result.Add(LastNumber);
   }
   else
   {
   while (LastNumber >= Base) {
    Garbage = LastNumber % Base;
    LastNumber = LastNumber / Base;
    if (LastNumber < Base)
    {
     result.Add(Garbage);
     result.Add(LastNumber);
    }
    else
    {
     result.Add(Garbage);
    }
   }
   }

   return result;
  }
  private string ListToString(List<int> Lista)
  {
   string Out = "";
   Lista.Reverse();
   foreach (int Numero in Lista) {
    if (Numero < 10)
    {
     Out += Numero.ToString();
    }
    else
    {
     Out += NumberToLetra(Numero);
    }
   }
   return Out;
  }
  public FrmMain()
  {
   InitializeComponent();
  }
  private char NumberToLetra(int Number)
  {
   return (char)(Number + 55);
  }
  private void FrmMain_Load(object sender, EventArgs e)
  {
   TextBoxes.Add(txtBase2);
   TextBoxes.Add(txtBase3);
   TextBoxes.Add(txtBase4);
   TextBoxes.Add(txtBase5);
   TextBoxes.Add(txtBase6);
   TextBoxes.Add(txtBase7);
   TextBoxes.Add(txtBase8);
   TextBoxes.Add(txtBase9);
   TextBoxes.Add(txtBase10);
   TextBoxes.Add(txtBase11);
   TextBoxes.Add(txtBase12);
   TextBoxes.Add(txtBase13);
   TextBoxes.Add(txtBase14);
   TextBoxes.Add(txtBase15);
   TextBoxes.Add(txtBase16);
   TextBoxes.Add(txtBase17);
   TextBoxes.Add(txtBase18);
   TextBoxes.Add(txtBase19);
   TextBoxes.Add(txtBase20);

  }

  private void BtnConvert_Click(object sender, EventArgs e)
  {
   for (int T = 0; T < TextBoxes.Count; T++)
   {
    TextBoxes[T].Text = ListToString(DecToAny(int.Parse(TxtNIn.Text), (T + 2)));
   }
  }
 }
}
El código anterior consta de 3 funciones. La función que hace gran parte del trabajo es "DecToAny", en ella se crean dos variables. La variable "LastNumber" será la encargada de almacenar el resultado de las divisiones anteriores. La variable "Garbage" será utilizada para guardar los "residuos" de las divisiones, estos son usados para formar el número final. Antes de siquiera empezar con el ciclo while, se hace una comprobación; si el número es menor que la base, simplemente se regresa una lista del tipo "int" con un solo elemento, en este caso, el mismo numero que se ingreso cómo parámetro para "Number". Si por el contrario el numero es igual o mayor que la base; se entra en el bucle while y  se comienzan a hacer las divisiones. En el bucle, se comprueba que el resultado de la división que esta almacenado en "LastNumber" sea mayor que la base. Cuando la base es mayor que el resultado de la división, el residuo de esta se agrega a la lista "result", pero cuando la condición ya no se cumple, entonces agrega a la misma lista el valor residual de la división y además el resultado de la misma. Finalmente devuelve una lista del tipo "int" con todos los valores.
La funcion "ListToString" se usa para convertir la lista del tipo "int" en un string (valga la redundancia). Para empezar, primero se invierte el orden en el cual están los elementos, hay que recordar que el resultado de las divisiones se usa en orden inverso en donde el primer digito, sera el residuo de la ultima division seguido del resultado entero de la misma seguido de los residuos del resto de las otras divisiones. Para devolver el resultado final, tambien se hace uso interno de la funcion "NumberToLetra", hay que recordar que para el decimo digito se usa la letra A.
Una vez que se ejecuta la función "DecToAny" hay que pasársela cómo parámetro a la función "ListToString" y el resultado será un string con una cadena equivalente al numero en la base especificada.

A tomar en cuenta.

Algo muy importante a tomar en cuenta es que las letras que se usan representan los valores más allá del decimo digito. Para el decimo digito usamos la A y asi sucesivamente hasta la Z (incluso más) La siguiente tabla muestra los valores disponibles.
ABCDEFGHIJKLMNOPQRSTUVWXYZ
1011121314151617181920212223242526272829303132333435
Entonces la base más grande que podemos usar es 35, pero eso solo para mantener humanamente legible cualquier número,pero podríamos seguir con el siguiente carácter ascii (que es el que usamos) y así el número Z0 sera algo totalmente diferente a z0.

Cómo de costumbre el código fuente completo lo puedes descargar de mi dropbox.
Por ahora es todo, los leo luego

Vamos a programar #34 - Depurando código de javascript.

Hola de nuevo a todos, el día de hoy vamos a ver una parte esencial para cualquier persona que se dedica a programar.

Cuando se crea un código nuevo, este siempre estará propenso a errores, está más que garantizado, que sin importar que tanto tiempo lleves en la programación, siempre surgirán errores. Algunos de ellos serán tan simples cómo: "olvide una coma", otros no tan obvios y solo surgirán al momento que se ejecute el código.
Para estos caso, en la mayoría de entornos de desarrollo siempre dispondremos de una herramienta que se encargara de dejarnos ver que es lo que está pasando en determinado punto del programa.

Suponiendo que queremos ver que está pasando en determinado punto del programa, en javascript; existe la palabra reservada "debugger", cuando la ejecución del programa se encuentra con esa palabra, la ejecución se detendrá y mostrará en la consola los datos correspondientes a las variables.

Para que la ejecución del depurador ocurra, el modo desarrollado debe de estar activado, para hacerlo en Google Chrome, basta con oprimir la tecla F12 (y se abrirá un ventana incrustada cómo en la primer imagen). En lo personal, prefiero usar Firefox, para acceder al modo desarrollador, basta con hacer clic en menú>Desarrollador > Depurador. Para el caso de Opera, debes de hacer clic en menú>Desarrollador>Herramientas de desarrollo.

En la siguiente imagen se muestra las partes importantes de la ventana de depuración:

El area con un recuadro rojo (1), son los controles que no servirán para controlar el flujo del código, existen cuatro botones y cada uno hace en orden de aparición lo siguiente:

  • Continua con el flujo normal del programa; es decir: ejecuta el programa hasta salir de la función o parte de código en la cual se invocó el depurador.
  • Las siguientes sirven para avanzar de "uno por uno", cada vez que se oprima, el programa avanzará una linea en el programa, si al momento de hacerlo se encuentra con la llamada a una función, el cursor se desplazará hasta ella y continuará la ejecución en la parte donde empieza, si se continua, avanzará sobre las lineas de la función y cuando acabe, volverá a donde se encontraba pero avanzando a la siguiente linea después de la ejecución de la función. Ademas al hacer esto, en la parte enmarcada en verde (2) todas las variables que se creen o cambien se mostraran ahí.
  • El último botón ejecutara hasta el final del bloque de código y al igual que el anterior, si hay cambios en las variables, se mostrarán en el panel Variables.
Todo esto sirve para revisar paso a paso lo que sucede en cada variable al momento de que se realiza la ejecución. Supongamos que un días llegas ebrio y decides agregar unas cuantas lineas de código, cuando recuperes tu sobriedad, probablemente puedas identificar la mayor parte del código que agregaste, pero si por alguna razón, una linea pasa desapercibida; pueden ocurrir muchos dolores de cabeza (y no lo digo cómo anécdota).

Cuando los errores se detectan, que es lo que se debe de hacer? usualmente los errores que se encuentren, se mostraran en un apartado llamado "consola web"


Por cada error habrá una pequeña descripción acerca de este, en el caso de la imagen, Nos dice que hay un error en la linea 183; nos dice que al tratar de obtener un elemento por su ID, este nos devolvió "NULL", eso puede significar que el objeto que estamos tratando de obtener no existe, pero Nosotros sabemos que si está, al revisar la linea vemos que el código es: "document.getElementById('ResulT').value = Salida;", al revisar los objetos que creamos en el formulario vemos que no hay ningun elemento que tenga esa ID y el mas cercano es "Result". Entonces eso significa que cometimos un error al escribir el ID del objeto que queríamos y en su lugar, mandamos a llamar a otro que no existe. Al cambiar el ID por el correcto, el error desaparece.

Con esas herramientas resultará mas sencilla la tarea de eliminar bug's de nuestro código.
Por el momento es todo, en próximos post, veremos cómo es el proceso de depuración en otras plataformas.

Los leo luego.