El desafío Chuck Norris... resuelto

Published 32 weeks, 3 days ago
Tue Nov 20 2007

El viernes pasado la empresa Ilitia Technologies, a la que conozco por ser la empresa de uno de mis blogueros de cabecera, Miguel Jiménez, publicó en Infojobs la siguiente oferta (editada ligeramente, que Infojobs no admite HTML):

Un importante proyecto de decodificacion de mensajes para el gobierno que fúe 
desarrollado por el mismisimo Chuck Norris (que escribe cualquier programa en una sola linea) 
necesita ser puesto en marcha lo antes posible. Lamentablemente, Chuck Norris eliminó del control 
de codigo fuente su libreria de utilidades antes de irse de la empresa para probar 
nuevos retos, por lo que el código ya no compila.
Es necesario que alguien reconstruya las partes necesarias de esta libreria para volver a hacer 
funcionar el decodificador. Una pista, nadie hace librerias tan simples como Chuck Norris.

Ánimo y gracias por tu interés y tiempo. Espero te diviertas

Console.WriteLine(
new long[]{
292805444303323170,
-4455112766532738942,
579137309091315972,
1207248581508040306,
7033655460037132288 }
.SelectMany(l => 64.DownTo(0)
.Select(i => (l & ((long)1 << i)) != 0))
.Take(285)
.Select((b, i) => new { b, i })
.GroupBy(p => p.i / 3, p => p.b)
.Select(g => g.ToArray())
.Select(a => 0.To(3)
.Sum(i => a[i] ? (1 << (2 - i)) : 0))
.Select((a, i) => new { c = @" _()|/'\,"[a], i })
.GroupBy(p => p.i / 19, p => p.c)
.Select(g => new string(g.ToArray()))
.Aggregate((s, t) => s + "\r\n" + t));

public static class ExtensionesChuckNorris
{
public static IEnumerable.int To(this int a, int b)
{
//TODO: Completar código
}
public static IEnumerable.int DownTo(this int a, int b)
{
//TODO: Completar código
}
}

La oferta, original como pocas, recibió sus 15 segundos de fama (estoy seguro de que si Warhol hubiera conocido Internet, la frase hubiera sido en segundos) siendo vinculada por unos cuantos blogs y foros. A mí me interesaba más resolverla, la verdad. Así que llegué a casa el viernes por la noche y me puse a ello. Si le echamos un vistazo por encima (y somos de los que, al menos, leemos blogs) podemos ver que en ese pedazo de bloque de código aparentemente ilegible se declara un array de tipo long, que se rellena con un montón de números aparentemente sin sentido a los que a continuación y sin un pobre punto y coma que llevarse a la boca se le ejecutan un montón de operaciones con nombres como Select, GroupBy, o Aggregate. Esto tiene toda la pinta de ser LINQ. Primer problema: no tengo ni repajolera idea de LINQ.

Don't panic

Bueno, me dije, ignora ese bloque de momento. Sabes que es un array de tipo long al que se le hacen un montón de transformaciones mediante LINQ. Puedes suponer que el resultado no van a ser números sino caracteres, por la presencia de un sospechoso cast a String y un par de cadenas. Bien. ¿Qué pide exactamente el problema? Concretamente, nos piden que implementemos dos de esas transformaciones: To(int a, int b) y DownTo(int a, int b). Mirando en el código principal, vemos que las llamadas a estas funciones son 64.DownTo(0) y 0.To(3). Es una sintaxis un tanto extraña, y aparentemente no tiene nada que ver con la firma de las funciones, pero puedo suponer que 64.DownTo(0) es equivalente a DownTo(64, 0). DownTo se podría traducir por Hasta, y teniendo en cuenta que el tipo que debe devolver la función es un IEnumerable<int>, podemos suponer que DownTo debe devolver un rango de números desde a hasta b en orden descendente, y que la función To debe devolver un rango de números desde a hasta b en orden ascendente. Vale, ya entendemos el problema. Intentemos implementarlo.

Intento y error

LINQ está disponible a partir de la versión 3.5 del .NET Framework, y sinceramente no tengo ganas de ponerme a buscarlas e instalarlas. El problema de las betas, CTPs, RTMs y demás versiones de early-adopters es que suelen ser algo puñeteras en su instalación y configuración, y el problema ahora era de código, no tenía ganas de comerme la cabeza con configuraciones. Virtual PC 2007 con la imagen completa de Windows 2003 Server y la última Beta de VS2008 al rescate. Sí, es una descarga de 11 Gbs, pero para hacer pruebas locas es perfecto. Una vez tenemos las herramientas necesarias (aunque eché bastante de menos a mi ReSharper, a ver cuándo hacen versión para el 2008), nos ponemos a currar.

Intenté primero hacer las cosas a lo sencillo: un simple bucle for que creara un array de integers desde a hasta b, convertirlo a IEnumerable<int> y a tirar. Pero no hubo manera humana de hacerlo, o yo no la encontré... y menos mal, porque aunque hubiera funcionado ésa no era la respuesta. Tenía que haber una manera de hacerlo mediante LINQ.

Después de mucho googlear, me encontré con unos cuantos recursos valiosos, entre ellos esta wiki sobre LINQ, y sobre todo este artículo de MSDN, que me pusieron sobre la pista correcta. Concretamente, conocí la existencia de la clase System.Linq.Enumerable, concretamente de su operador Range que, mira por dónde, devuelve un rango IEnumerable<int>. Parece que la función To estaba resuelta. ¿Y la función DownTo?


Al principio intenté unas cuantas manipulaciones groseras de Range, pero recordé que el reto me pedía soluciones sencillas, pues tal es el estilo de programación de Chuck... cosa que, viendo sus películas, dudo mucho. Pero ésa es otra cuestión. Seguí leyendo e investigando y me encontré con otro operador de System.Linq.Enumerable, el Reverse, que efectivamente era la solución que andaba buscando. Un poquito de intento y error para hacer las cosas lo más limpias posibles y voilá!: el reto de Chuck está solucionado.

Aquí tenéis el código completo:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ilitia
{
class Program
{
    static void Main(string[] args)
    {
      Console.WriteLine(
            new long[]{
            292805444303323170,
            -4455112766532738942,
            579137309091315972,
            1207248581508040306,
            7033655460037132288 }
            .SelectMany(l => 64.DownTo(0)
            .Select(i => (l & ((long)1 << i)) != 0))
            .Take(285)
            .Select((b, i) => new { b, i })
            .GroupBy(p => p.i / 3, p => p.b)
            .Select(g => g.ToArray())
            .Select(a => 0.To(3)
            .Sum(i => a[i] ? (1 << (2 - i)) : 0))
            .Select((a, i) => new { c = @" _()|/'\,"[a], i })
            .GroupBy(p => p.i / 19, p => p.c)
            .Select(g => new string(g.ToArray()))
            .Aggregate((s, t) => s + "\r\n" + t));
            
      Console.ReadLine();
    }
}

public static class ExtensionesChuckNorris
{
    /// <summary>
    /// Devolvemos un rango enumerable desde inicio a fin.
    /// </summary>
    /// <param name="a">Valor inicial</param>
    /// <param name="b">Valor final</param>
    /// <returns>Un rango ascendente</returns>
    public static IEnumerable<int> To(this int a, int b)
    {
        return Enumerable.Range(a, b);
    }

    /// <summary>
    /// Devolvemos un rango inverso enumerable desde fin a inicio.
    /// </summary>
    /// <param name="a">Valor inicial</param>
    /// <param name="b">Valor final</param>
    /// <returns>Un rango descendente</returns>
    public static IEnumerable<int> DownTo(this int a, int b)
    {
        //Invocamos To y le damos la vuelta
        return Enumerable.Reverse(b.To(a));
    }

Y éste es el resultado por pantalla:

Y esto es todo, amigos. Gracias a Fernando Lozano, gerente de Ilitia, que amablemente me ha dado permiso para escribir este post al haber terminado el reto y no aceptar más candidaturas para esa oferta.

Otro, otro!

Published 36 weeks, 8 hours ago
Sat Oct 27 2007
Rob Conery, el padre de Subsonic ha sido contratado por Microsoft. Y creo que esto es otra gran noticia.

¿Por qué?

Sencillamente, porque Rob ha sido contratado para seguir haciendo lo que más le gusta: trabajar en Subsonic, ya que (y ésta es la gran noticia) Subsonic va a formar parte oficialmente de MVC.NET, la implementación MVC que Microsoft presentará el año que viene.

Lo que viene a confirmar lo que Jon Galloway ya dijo en su día y yo estuve inmediatamente de acuerdo nada más probar Subsonic: esta herramienta debería estar al alcance de todo el mundo, incluidos esos programadores 501, que no leen libros, ni blogs ni usan Open Source ni nada que no venga empaquetado directamente en Visual Studio.

¡Enhorabuena, Rob!

Nothin' But .NET en Londres

Published 52 weeks, 10 hours ago
Sat Jul 07 2007

Jean-Paul Boodhoo, es un MVP, Agilista, Extreme Developer, y en general una máquina de tío; se dedica entre otras cosas a impartir unos cursos especiales que llama Nothin' But .NET. Montados en plan campamento de instrucción, son cinco días de curso intensivo con jornadas de entre 8 a 14 horas de trabajo, durante los cuales se monta una aplicación .NET de principio a fin con especial énfasis en el diseño, código, uso de patrones de diseño, testeos unitarios y desarrollo ágil.

Llevo cierto tiempo suscrito a su blog y siempre he visto con envidia cómo otros programadores, generalmente en Estados Unidos, tienen la oportunidad de asistir a uno de estos BootCamps. Pero ahora se me ha puesto (relativamente) a tiro: en Septiembre, Jean-Paul Boodhoo imparte uno de sus cursos en Londres.

Y digo lo de relativamente porque, a no ser que me toque la lotería ya mismo, no voy a poder ir. Sólo el curso cuesta $4000 (2940€), a lo que hay que sumar la estancia en hotel. El mismo hotel donde se va a celebrar el curso ofrece un descuento especial a los asistentes al mismo, gracias al cual pagas sólo 264€/noche, sin VAT. Sin incluir el coste del vuelo y otros gastos, que ya no tenía ganas, y sin calcular el VAT, la broma se pone en 4260€.

Me temo que no voy a poder ir, no.

Por si alguno se lo plantea, que sepáis que curiosamente, en Nueva York en Octubre el curso es $1000 más barato, a no ser que sea una errata...

ReMIX '07 (II)

Published 56 weeks, 2 days ago
Wed Jun 06 2007

La primera sesión del martes, segundo y último día del ReMIX '07 en Madrid, empezó con la ponencia de Arturo Toledo sobre estándares Web y su uso en Expression Web. Sinceramente, fue un poco aburrida: creo que Arturo dedicó demasiado tiempo a hablar de cosas demasiado básicas como XHTML y CSS y luego le quedó poco tiempo para demostrar cosas con un poco más de fundamento, como las capacidades de Expression Web para las transformaciones XSL. Por desgracia, me enteré ayer por la tarde de lo que ha comentado Jon Galloway en su blog: el formato estándar SVG es incompatible con Expression. Demasiado tarde, por tanto, para preguntar en el ReMIX sobre esto. Afortunadamente, Arturo Toledo estaba repartiendo tarjetas de visita como si fueran caramelos, y fue en todo momento muy amable y nos instó a todos a que lo torpedeáramos a preguntas, dudas y sugerencias sobre la suite Expression; así que le he tomado la palabra y le he preguntado sobre este particular por correo. A ver qué me responde, cuando vuelva de sus merecidas vacaciones. =)

MiGs in Action!Después de la sesión de Toledo, Miguel Jiménez nos dió caña con una sesión sobre AJAX y patrones. Mediante un ejemplo de Accordion picado a mano (es decir sin usar el incluido en el AJAX Toolkit) Miguel nos demostró las bondades de la separación de lógica y presentación, llegando al mejor extremo: encapsular todos nuestros scripts y demás lógica en un extender de control de servidor. También nos enseñó a manipular, y crear, para aquellos controles AJAX que no lo hacen automáticamente, el historial de navegación de la página, usando JavaScript y un control de las futures de AJAX. Un control desarrollado por él llamado UpdateIndicator, necesario porque a veces los controles AJAX responden demasiado bien y el usuario no se ha enterado de que ha pasado algo. Habló también sobre JSON, mashups, consumición de servicios, etc...

 Después de la sesión de Miguel, tocaba una charla sobre cómo consumir servicios de Microsoft como Live, búsquedas, etc. Sinceramente, yo estaba bastante machacado y tenía ganas de que me diera el aire, comer tranquilamente y esas cosas. Así que me escaqueé de la sesión, cogí la Gran Vía para arriba y terminé, como no podía ser de otro modo, en la Casa del Libro:The Pila! donde, claro está, piqué. De arriba a abajo, los libros son:

  • Los viajes de Tuf, de George R. R. Martin.
  • The Prestige, de Christopher Priest. La película me encantó, y me llevaban meses hablando bien del libro.
  • Fight Club, de Chuck Palahniuck. 'Nuff said.
  • The Terror, de Dan Simmons. Me leo todo lo que cae en mis garras de este hombre, y raramente me ha defraudado. Éste es sobre la expedición de Sir John Franklin, en los barcos Erebus y Terror. Promete.
  • The Armageddon Rag, otro de George R. R. Martin, de quien también me leo todo lo que pillo.
  • Por último, ese tomaco con florecillas en la portada no es un manual de horticultura ni una novela romántica, sino lo último del maestro Stephen King: La historia de Lisey. Como casi siempre, demoledor.

Por cierto, qué bien se está sentado a la sombra en un banquito, leyendo con mi cigarrito sin prisas, sin agobios; y viendo a la gente pasar. Ay, por un rato me dió la impresión de estar de vacaciones. Ah, espera: estaba de vacaciones... XD

Pero estoy desbarrando.

Al volver de comer, volvimos a vernos las caras con David Carmona y sus demostraciones sobre WPF. Posiblemente sea porque mi experiencia previa ha sido principalmente con el escritorio y programar para la Web no me gusta y nunca me ha gustado, posiblemente sea porque el resto de las tecnologías presentadas en el ReMIX me van a obligar a estudiar mucho JavaScript, al que odio cordialmente. Vete a saber porqué razón es, pero lo cierto es que las demos de WPF fueron, con mucho, lo que más me ha impresionado de este ReMIX '07. Es más, sabiendo que vas a poder tener en el escritorio aplicaciones con interfaces vistosos, visuales, dinámicos, vivos, en tres dimensiones, que podrán auto-actualizarse (como cualquier otro programa .NET) gracias a ClickOnce, que podrán consumir servicios Web, etc., etc. la duda que me asalta no es si el escritorio está muerto, como dicen Joma y Joel Spolsky; es cómo puede querer cualquiera programar para la Web. ¿Teniendo descargado un lector WPF como el del NYTimes o el de Marca, autoactualizable, con funcionalidad offline, y todo el eye-candy de WPF, quién demonios va a ir a su página web?

Pero sigo desbarrando.

David Carmona nos contó, y nos demostró, cómo hacer enlaces a datos en WPF; cómo hacer bordes que se apliquen a cualquier otro control de nuestra aplicación, cómo hacer propiedades propias para un Canvas (el equivalente a un control Panel en WPF) y hacer que todos los controles que están dentro de ese Canvas adquieran esas propiedades. Al hilo de esto, una demo de lo más simple que me dejó con la boca abierta: creó un control llamado Panel Radial e introdujo un simple control ListBox de los de toda la vida dentro de él. Por estar dentro de un panel radial, los elementos del ListBox se mostraban en círculo, de forma radial. Tan simple como eso. Estuvo mostrándonos algunos controles de teceros ya en desarrollo para WPF (aquí el que no corre, vuela, demonios) y hay auténticas virguerías. En fin, os aseguro que WPF es el futuro. Yo no soy ningún gurú, no soy ningún Spolsky; pero imaginaos el escenario: aplicaciones empresariales con toda la potencia de .NET y toda la capacidad de maniobra (más, incluso) de Flash pero en el escritorio. Se venden solas. Me las quitan de las manos, señora.

Por último y como cierre del ReMIX, David Salgado nos hizo otra contundente demo de AJAX avanzado. Nos cuenta cómo debemos distinguir entre el uso condicional o no del refresco de un UpdatePanel, David DJ! como podemos forzar el refresco de un UpdatePanel mediante triggers, cómo depurar JavaScript hoy mismo con Visual Studio .NET (haciendo alguna instalación extra, aunque Orcas traerá compilación JS de serie) y los nuevos espacios de nombres (Sys.Debug) para JavaScript creados por Microsoft, prototipos, PageMethods, JSON y un montón de cosas más que debo confesar que no entiendo del todo y que hacen que se refuerce mi impresión de que, para mi desgracia, al final voy a tener que aprender JavaScript. Sigh.

Resumiendo, el ReMIX ha sido una buena ocasión para ver por dónde van a ir los tiros próximamente y qué es lo que hay que ponerse a practicar ya. Al turrón, caballeros.

Actualización 08/06/2007.- Arturo Toledo me ha respondido. Ante todo, quiero agradecer y admirar la dedicación, profesionalidad y amabilidad de una persona que se toma el tiempo a responder a preguntas impertinentes estando de vacaciones: ¡gracias, Arturo! Por desgracia, Arturo me confirma lo que había adelantado Jon Galloway: Expression Studio no soporta SVG out-of-the-box. Aunque lógicamente podremos utilizar ficheros SVG en las páginas que diseñemos mediante Expression Web, no podremos modificarlos mediante Blend. No creo que tarde mucho nadie en crear un buen conversor, por otra parte.

Actualización 13/06/2007.- Aunque todavía no hay nada, ésta es la dirección en la que próximamente se colgarán los vídeos que nos prometieron...

ReMIX '07 (I)

Published 56 weeks, 4 days ago
Mon Jun 04 2007

Acabo de llegar a casa del primer día del ReMIX '07 y lo prometido es deuda: os cuento.

A primera hora de la mañana, o al menos eso es lo que me parecía por ser un día de vacaciones para mí, hemos tenido la presentación del evento (lo que los yankees llaman Keynote) en las que nos han contado rápidamente no ya la que se nos avecina, sino lo que ya tenemos aquí: Silverlight para la Web y WPF para el escritorio; ambos enfocados en dar a nuestras aplicaciones interfaces de usuarios ricos y vistosos, todo ello con Microsoft .NET como el motor principal.

Se han celebrado sesiones en paralelo en dos salas distintas del Círculo de Bellas Artes, y os recuerdo que podéis consultar la agenda completa aquí. Como, a pesar de tener la masa suficiente para ello, aún no he aprendido el truco de desdoblarme, he tenido que decidirme por unas charlas en lugar de otras. Habida cuenta de que nos han dicho que las sesiones en inglés serán publicadas en vídeo más adelante, pero no así las de castellano, he decidido asistir a estas últimas y ya veré (y linkaré por aquí) las sesiones en inglés más adelante.

La primera presentación ha corrido a cargo de David Carmona, infatigable evangelista de Microsoft España, y ha sido una presentación de las de mirar qué chulada sobre aplicaciones que usan WPF. Destacar la que está desarrollando Indra para el seguimiento de regatas, el lector offline que Marca está desarrollando para su diario (al estilo del lector que ha creado el NY Times al alimón con Microsoft, y la más impresionante, una de NetFlix, mediante la cual podías ver sus películas (previa descarga, no mediante streaming) con un interfaz muy avanzado pero, y esto es lo que me impresionó, podías invitar a un amigo online a verla contigo.

David pasó el testigo a Arturo Toledo, Technical Product Manager de Microsoft para Expression, que nos hizo un par de precisiones más sobre Silverlight y WPF usando la suite de herramientas de la que es mánager técnico: Expression. Vimos una web preparada mediante una mezcla entre Silverlight y Javascript, mediante la cual podías jugar al ajedrez contra el servidor. Ya sé, poco impresionante. La gracia es que podías elegir enfrentarte contra el motor en Javascript o contra el motor en C#. Gracias a Silverlight, ambos respondían con la misma rapidez de cara al cliente pero en el mismo tiempo C# pensaba muchas más jugadas que Javascript, de tal manera que cuando pusieron a jugar a ambos motores uno contra el otro, ganó C# en muy pocos movimientos, lo que provocó la consiguiente carcajada del público. Arturo siguió contándonos como gracias a Silverlight podremos tener interfaces de usuario muy, muy ricos y con tiempos de respuesta prácticamente inmediatos, demostrando cómo podremos hacer cosas impensables hasta ahora en Web como la edición de vídeo online.

Después de un café, David Salgado, el otro infatigable evangelista de Microsoft Ibérica nos dió una charla en la que ya vimos chicha: fundamentos básicos de AJAX .NET. A la interesante pregunta de si Silverlight viene a acabar con el AJAX y el Javascript, a lo que yo pensaba que no habría tanta suerte, David respondió que parece que no: la tendencia parece tender (y la redundancia a redundar) hacia crear sitios Web con ASP .NET y AJAX, que en partes localizadas de su funcionalidad (islas) usen Silverlight. La charla de David fue muy interesante, pero básica: ScriptManager, UpdatePanels, control Extenders, etc. Mañana veremos más chicha, seguro.

Pequeña pausa para cigarrete, servicios y demás; y vuelta a la carga: David Carmona nos hace una demo de creación de una aplicación WPF usando Expression Blend, potentísima herramienta de Microsoft que no he probado hasta ahora por primero, falta de tiempo y segundo pensar (erróneamente) que es una herramienta sólo para diseñadores. Sin tirar una sola línea de código y usando únicamente Expression Blend David nos ha construido en un rato una aplicación para ver fotos francamente chula. Y lo que estaba pensando este picacódigos en todo momento es que la mejor herramienta para presentar y vender WPF es precisamente el propio Expression: tiene una interfaz de usuario alucinante.

Pausa para la comida, y al turrón! Arturo Toledo again nos explica que él es más diseñador que desarrollador y nos muestra las posibilidades de uso que tiene Microsoft Expression Design, que es la parte de la suite que se dedica al diseño de gráficos vectoriales. Hemos visto las capacidades para hacer animaciones, para crear guías tomando como base cualquiera de los vectores de nuestro gráfico, publicar dichas animaciones directamente a la Web, etc.

Debo confesar que he sido un pesao: al final de todas las sesiones me he acercado al escenario y les he hecho alguna pregunta a los ponentes. Que conste que me ha costado porque siempre que voy a estas presentaciones entro en modo soy un inútil picacódigos rodeado de gigantes... no soy digno, no soy digno (y no estoy siendo irónico, de veras que me pasa esto) y siempre he tenido miedo de abrir la boca y preguntar... y, por supuesto, son gente muy amable, muy atenta y muy maja. Me han resuelto todas mis dudas e incluso han aguantado cuando esas dudas han generado otras. También he podido conocer a uno de mis code ninjas favoritos, Miguel Jiménez, con el que por desgracia no pude tener mucho tiempo para charlar. Miguel, si lees esto, que sepas que si no paso por las reuniones del Mad·NUG es porque no tengo coche y las oficinas de Microsoft Ibérica me pillan lejísimos. Y alegraos, en caso contrario os estaría dando el coñazo cada semana. =)

En fin, mañana seguiremos informando.

Nota.- Tenía una foto para publicar con este post, pero la calidad es patética. Nota mental: cómprate una cámara de fotos, el móvil no vale para estas cosas. Al menos, el mío no. =)

Surface

Published 57 weeks, 2 days ago
Thu May 31 2007

Similar al BumpTop del que ya hablamos por aquí en su día, ahora Microsoft desvela Surface: en esencia un hardware futurista en forma de mesa. La superficie de la mesa es una gran pantalla táctil que, según los vídeos de presentación, permitirá manejar los programas disponibles en el mismo mediante las manos, pinceles o prácticamente cualquier otra cosa.

El site, realizado por cierto íntegramente con Silverlight (*), nos muestra tres vídeos de presentación en los que vemos las posibilidades del cacharro: usando Silverlight para todo, vemos una serie de programas que permiten manipular imágenes, crearlas, enviarlas por correo, planear viajes mediante mapas interactivos, manipular álbumes de música y crear listas de reproducción, etc. Todo muy bonito, muy de diseño, con el dedo y muy Web 2.0.

Pero lo que realmente me ha dejado boquiabierto son las capacidades de sincronización que muestran los vídeos. No creo que realmente funcionen así ahora mismo, pero auguran un futuro prometedor. Ved los vídeos, que merecen la pena.

Y otra cosa que me alegra: el software que maneja Surface es íntegramente .NET con Silverlight. Microsoft (¡por fin!) empieza a predicar con el ejemplo y a utilizar .NET en sus productos. Ya era bendita la hora. 

 

(*) Actualización 16:25.- Como bien me avisan en los comentarios, ¡está hecha en Flash! Decepcionante MS por no predicar con el ejemplo y usar sus propios productos...

Cajón de herramientas

Published 71 weeks, 4 days ago
Mon Feb 19 2007
Respondiendo a los comentarios de hace algunas entradas, lo prometido es deuda. Aquí tenéis la lista de mis herramientas favoritas para .NET. salvo que se indique lo contrario, todas las herramientas que menciono son gratuitas y la mayoría son Open Source.
  • Resharper.- Tuvo su propia entrada no hace mucho, así que no voy a añadir nada más. Sólo que, si sois usuarios de este fantástica herramienta, os interesará saber que Joe White ha iniciado una serie-tutorial al respecto que está francamente interesante. No, por desgracia no es gratis.
  • Data Dictionary Creator.- Creado por Jon Galloway y esponsorizado por la compañía en la que trabaja (Veloc-IT, cómo molaría trabajar en una empresa que no sólo alentase sino que promoviese los pet projects, eh? Pero esto es España, muchachos! Que investiguen otros!!). Se trata de un documentador de bases de datos, simple pero funcional. Open Source.
  • Reflector.- Sigue siendo EL visor de clases por excelencia de .NET.
  • WinMerge.- No es estrictamente una herramienta .NET, pero más de una vez me ha venido de perlas para comparar diferencias entre ficheros web.config, por ejemplo. Open source.
  • Inno Setup.- Desde 1997, uno de los mejores programas para crear procedimientos de instalación para aplicaciones Windows de escritorio. Los instaladores son creados mediante el uso de un sencillo lenguaje de script, pero si no queréis ni siquiera esa mínima complicación siempre podéis usar ISTool, una herramienta también gratuita que nos permite crear y modificar scripts de Inno Setup.
  • Xml Notepad 2007.- Para la creación y modificación de ficheros XML, permitiendo especificar esquemas XSD o transformaciones XSLT. De Microsoft y gratuito, para pasmo de algunos.
  • SlickEdit gadgets.- Colección de plugins para Visual Studio, que incluyen el Command Spy, fantástica utilidad que cuando la activas puede decirte qué atajos de teclado se corresponden a las acciones que vas realizando con el ratón; el un analizador de objetos, un explorador de archivos integrado, un analizador de SLOC que te dice el total de líneas tiradas en la solución, desglosándolas por líneas en blanco, líneas de código y comentarios, y unas cuantas utilidades más para la ventana de edición de código de VS 2005, incluyendo la posibilidad de poner una imagen de fondo a nuestra ventana de código, lo que siempre queda muy bien :)
  • SmartPaster.- Simplemente, una idea fantástica: un plugin para VS que nos permite pegar el texto previamente copiado en el portapales como texto normal, o texto comentado, o como un string builder, o como una región. Sencillamente genial.
  • TimeSnapper.- Aunque en esencia no es una herramienta de programación, TimeSnapper permite controlar lo que hacemos al cabo del día y optimizar nuestro rendimiento, quedándose residente en memoria y haciendo capturas de pantalla de nuestro escritorio a intervalos configurables, para después reproducirlos todos y ver en qué nos ha cundido (o no) el día. Versión lite gratuita, existe una versión profesional por $39.95.
  • CodeColorizer, la herramienta que uso para poner colores al código fuente que copio y pego en este mismo blog. Por cierto, debo ser el único que lo usa (o que respeta sus reglas), porque si buscáis en Google "CarlosAG.CodeColorizer" salgo justo detrás de la web correcta, y varias veces. =)
  • Snippet Compiler, de Jeff Key. Impresionante herramienta para testear pequeños fragmentos de código .NET, sin necesidad de tener que crear una solución completamente nueva cada vez que se te ocurre una idea.
  • StickyNotes. No confundir con Sticky-Notes, que es otro programa para lo mismo, pero no es el mismo programa. Hay muchos más programas de este estilo, pero éste es mi favorito: lo uso para guardar copypasteos, para apuntarme recordatorios, poner alarmas, etc... La verdad, no podría vivir sin él, al margen de la programación.

Y ya está. He probado otros muchos, pero estos son los que uso prácticamente todos los días. Si tenéis alguna sugerencia, me he olvidado de alguno o queréis publicitar vuestra herramienta entre mis cuatro o cinco lectores :), vuestros comentarios son bienvenidos.

Actualización 23/02/2007.- Corregida la entrada para añadir las StickyNotes, y enmendar así una gran injusticia...

I Love ReSharper

Published 75 weeks, 3 days ago
Wed Jan 24 2007
Ésta iba a ser una entrada sobre los distintos plugins de VS 2005 que uso, y otros programas útiles para mi día a día (la mayoría Open Source, por cierto); pero mientras lo estaba escribiendo se ha ido convirtiendo progresivamente en una carta de amor a Resharper. No puedo más que decir cosas buenas de este plugin de aumento de productividad.

Éstas son sólo algunas de sus características:
  • Chequeo de errores y avisos según escribes código; al estilo de la revisión automática de ortografía de Office pero bastante más inteligente.
  • Intellisense mejorado. De lejos, lo que más me gusta y más se nota después de recién instalado. Si lo deseas, ReSharper puede reemplazar el Intellisense de VS2005 con el suyo propio, que es más rápido y completo. Posibilidad de establecer en Intellisense la misma fuente que en el entorno de desarrollo, posibilidad de reemplazar los iconos de Intellisense con los de ReSharper, añade un pop-up con la firma del método completo al resaltarlo en Intellisense y se activa desde que escribes los primeros tres caracteres (por defecto) de cualquier cosa.
  • Auto-completado de código, escribes las tres primeras letras de cualquier cosa y ReSharper ya te está mostrando alternativas. Si además lo que estás escribiendo es un método, pulsando TAB te inserta el nombre completo del método más los paréntesis y el punto y coma necesario.
  • Templates.
  • Refactorización: extracción de propiedades desde variables, extracción de métodos desde código: seleccionas un bloque de código, eliges extraerlo como método, le das un nombre y opciones, entre las que se incluyen los parámetros de entrada del método que ReSharper detecta automáticamente y método extraído.
  • Búsqueda avanzada de usos de métodos.
  • Vista de jerarquías de tipos.
  • Lanzamiento de testeos unitarios con NUnit o csUnit desde el propio IDE.
  • Acciones contextuales. En ciertas secciones del código puede aparecer una pequeña ventana flotante con un icono de una bombilla. ReSharper te quiere ayudar: extendiendo esa ventana con el ratón (o mejor, ALT+Enter) ReSharper te mostrará una lista de las posibles acciones a realizar según el contexto del código: chequear valores nulos, reorganizar sentencias if, reorganizar cadenas con un StringBuilder, eliminar referencias using que no se están usando.
  • Detección automática de variables, métodos o referencias no usadas. Declaras una variable al principio del código y luego no la usas para nada: ReSharper la resalta en color gris, para que sepas que no se está usando en ningún sitio, ningún método hace referencia a ella.
  • Salto directo a la definición de un método o variable: con CTRL+Click encima del nombre de un método saltamos directamente al fichero de código que contiene ese método, si está disponible.
Y estos son sólo las partes de la herramienta que utilizo todos los días. Definitivamente, es la mejor compra que he hecho jamás para un complemento de Visual Studio; sobre todo teniendo en cuenta que me acogí a la oferta de $99 de estas navidades (de la que ya avisé por aquí), porque ha vuelto a su precio normal de $249. Los programadores de JetBrains hacen con su producto que Visual Studio 2005 sea un IDE aún mejor de lo que ya es. La lástima es que la perenne noticia de que están desarrollando su propio IDE para .NET es, al parecer, antigua (mirad en los comentarios), ya que al parecer el proyecto se ha abandonado. Una pena.

Apellidos aleatorios (y falsos)

Published 77 weeks, 21 hours ago
Fri Jan 12 2007
Respondiendo al desafío planteado por Jon Galloway en su excelente blog, aquí está mi solución para generar apellidos falsos y aleatorios. El código está repleto de comentarios, eso sí en inglés de momento, los traduciré en cuanto pueda. Estás invitado, por supuesto, a hacer preguntas, críticas, o incluso mejor un buen refactorizado! =)

class Program
{
    
//Un arraylist que contiene todos los posibles digramas (ver más abajo)
    //(Nota: digram en inglés es la combinación de dos caracteres, no sé si en español será sílaba)
    private static ArrayList AllDigrams = new ArrayList();
    
//Un arraylist que contiene todas las letras posibles (ver más abajo)
    
private static ArrayList AllLetters = new ArrayList();
    
//Una semilla aleatoria para el generador aleatorio principal
    
private static Random rSeed = new Random()
    
//Un array multidimensional que especifica la estructura principal de un apellido. 
    // "A" representa una vocal, "B" una consonante
    //"AB representa un digrama de vocal + consonante, etc...
    //Estas combinaciones las he hecho a mano, puedes añadir, modificar o quitar 
    //combinaciones para ajustar el tipo de apellidos que se obtengan
    
private static string[,] SurnameRandomTypes = new string[106]
            {
                {
"A",  "BA""B",  "A",  "BA"""},
                {
"B",  "A",  "BA""",   "",   ""},
                {
"A",  "BA""A",  "B",  "A",  "BB"},
                {
"A",  "B",  "B",  "A",  "BA"""},
                {
"A",  "BB""AB""A",  "",   ""},
                {
"BA""B""B",  "A",  "B",  "A"},
                {
"B",  "AB""A",  "BB""A",  "AB"},
                {
"B",  "AB""B",  "A",  "BB""AB"},
                {
"A",  "BB""A",  "B",  "AB"""},
                {
"AB""AA""B",  "A",  "B",  "A"},
            }
;

    static void Main()
    {
        
bool exit = false;
        
//Cargamos TODOS los posibles digrams en memoria, 
        //en el arraylist AllDigrams
        
LoadDigrams();
        
//Lo mismo para las letras posibles, 
        //en el arraylist AllLetters
        
LoadLetters();
        do
        
{
            
for (int 0i <20i++)
            {
                Console.WriteLine(
"Apellido Aleatorio:    {0}"
                    GenerateRandomSurname())
;
            
}
            Console.WriteLine(
"Pulsa ENTER para generar otro lote, X para salir");
            string 
sInput Console.ReadLine();
            if 
(sInput.ToString() == "x" || sInput.ToString() == "X")
                exit 
= true;
        
while (exit == false);
        
Console.ReadLine();
    
}

    
/// <summary>
    /// Éste método genera un apellido aleatorio y lo devuelve.
    /// </summary>
    /// <returns></returns>
    
static string GenerateRandomSurname()
    {
        
string sRet "";
        
Random r;
        
//Nueva semilla aleatoria para el generador de números
        
= new Random(rSeed.Next(11000));
        
//y obtenemos un índice al azar
        //para el array multidimensional SurnameRandomTypes
        
int rndIndex r.Next(09);
        
//Entonces leemos las distintas "columnas" de  
        //esa "fila" y decidimos si necesitamos una vocal, 
        //una consonante, un digrama vocal-consonante, 
        //o lo que sea
        
for (int 0i < 6i++)
        {
            
switch(SurnameRandomTypes[rndIndex, i])
            {
                
case "AA":
                    sRet +
getDigram(DigramType.TwoVowels);
                    break;
                case 
"AB":
                    sRet +
getDigram(DigramType.VowelAndConsonant);
                    break;
                case 
"BA":
                    sRet +
getDigram(DigramType.ConsonantAndVowel);
                    break;
                case 
"BB":
                    sRet +
getDigram(DigramType.TwoConsonants);
                    break;
                case 
"A":
                    sRet +
getLetter(LetterType.Vowel);
                    break;
                case 
"B":
                    sRet +
getLetter(LetterType.Consonant);
                    break;
                default
:
                    
break;
            
}
        }
        
//Y devolvemos el apellido generado 
        //convirtiendo a mayúsculas la primera letra
        
return sRet.Substring(0,1).ToUpper() + 
            sRet.Substring(
1);
    
}

    
#region Letters and Digrams
    
    
#region Structs and Enums
    
/// <summary>
    /// Con esta enumeración sabremos de qué 
    /// está compuesto el digrama
    /// </summary>
    
private enum DigramType
    { TwoVowels, VowelAndConsonant, ConsonantAndVowel, TwoConsonants }

    
/// <summary>
    /// Con esta enumeración sabremos si una letra es una
    /// vocal o una consonante
    /// </summary>
    
private enum LetterType { Vowel, Consonant }

    
/// <summary>
    /// Un objeto digrama. Además del propio digrama ("th", por ejemplo)
    /// guardamos el rango de frecuencia en el que el digrama aparece 
    /// en el lenguaje inglés y su tipo
    /// </summary>
    
private struct Digram 
    {
        
public int iniRange;        //El principio de su rango.
        
public int endRange;        //El final de su rango.
        
public string Value;        //Su valor
        
public DigramType Type;     //Su tipo, según la enumeración DigramType
    
}

    
/// <summary>
    /// Una letra. Además de la propia letra ("a", por ejemplo)
    /// guardamos el rango de frecuencia en la que aparece  
    /// en el lenguaje inglés y su tipo
    /// </summary>
    
private struct Letter
    {
        
public int iniRange;        //El principio de su rango.
        
public int endRange;        //El final de su rango.
        
public string Value;        //Su valor.
        
public LetterType Type;     //Su tipo, según la enumeración LetterType
    
}
    
#endregion

    
/// <summary>
    /// Este método devuelve el valor  
    /// de un digrama aleatorio del tipo especificado
    /// </summary>
    /// <param name="type">El tipo de digrama que queremos obtener</param>
    /// <returns>El VALOR del digrama ("th", por ejemplo)</returns>
    
static string getDigram(DigramType type)
    {
        Random r 
= new Random(rSeed.Next(01000));
        
Digram nuDigram = new Digram();
        do
        
{
            
//Obtenemos un número aleatorio dentro del rango
            //TOTAL de TODOS los digramas
            
int freq r.Next(05548)//Ver LoadDigrams()
            
foreach (Digram digram in AllDigrams)
            {
                
//Y si el número aleatorio está dentro del  
                //rango de frecuencia del digrama actual, 
                //tenemos un ganador!
                
if (freq >digram.iniRange && freq <digram.endRange) 
                        nuDigram 
digram;
            
}
            
//Pero sólo si es del tipo que necesitamos
        
while (nuDigram.Type !type);
        return 
nuDigram.Value;
    
}

    
/// <summary>
    /// Obtenemos el VALOR de una sola letra del tipo especificado
    /// </summary>
    /// <param name="type">El tipo de letra que queremos obtener</param>
    /// <returns>El VALOR de la letra</returns>
    
static string getLetter(LetterType type) 
    {
        Random r 
= new Random(rSeed.Next(01000));
        
Letter nuLetter = new Letter();
        do
        
{
            
//Obtenemos un número aleatorio dentro del rango 
            //TOTAL de TODAS las letras
            
int freq r.Next(010025)//Ver LoadLetters()
            
foreach (Letter letter in AllLetters)
            {
                
//Y si el número aleatorio
                //está dentro del rango de frecuencias de
                //la letra actual, tenemos un ganador!
                
if (freq >letter.iniRange && freq <letter.endRange)
                        nuLetter 
letter;
            
}
            
//Pero sólo si es del tipo que necesitamos  
        
while (nuLetter.Type !type);
        return 
nuLetter.Value;
    
}

    
/// <summary>
    /// Simple comprobación booleana para ver si una letra determinada es
    /// una vocal o una consonante
    /// </summary>
    /// <param name="checkLetter">La letra a comprobar</param>
    /// <returns>True si es una vocal, false en cualquier otro caso</returns>
    
static bool IsAVowel(string checkLetter)
    {
        
switch(checkLetter.ToLower())
        {
            
case "a":
            
case "e":
            
case "i":
            
case "o":
            
case "u":
                
return true;
            default
:
                
return false;
        
}
    }

    
/// <summary>
    /// Gracias del departamento de Informática de la Universidad de Bristol
    /// (y a Google) he encontrado una lista de todas las letras del
    /// alfabeto inglés, con su frecuencia de uso.
    /// Como Random maneja enteros, he multiplicado todos estos valores * 100
    /// La suma de todos ellos debería ser 10.000, lo que hace que cuando calculamos
    /// el rango de números debería ser 10.000 + la cantidad de letras posibles;  
    /// porque el rango de la primera letra es de 0 a 1.231, y el siguiente  
    /// rango de letra empezará en 1.232 (el fin del anterior rango +1)
    /// En el caso de letras solas esto es correcto, y los rangos van desde
    /// 0 a 10.025. 
    /// 
    /// Pero, no sé porqué, en el caso de los digramas el rango va  
    /// de 0 a 5.548. El porcentaje de frecuencia de todos los digramas disponibles,
    /// todos ellos multiplicados por 100 y sumados NO da 10.000 como debiera.
    /// ¿Alguien sabe porqué? A no ser que la lista de digramas que tengo NO esté completa.
    /// </summary>
    
static void LoadLetters()
    {
        
// http://www.cs.bris.ac.uk/Teaching/Resources/COMS30124/Labs/freq.html
        // Porcentaje de frecuencia de letras
        //
        //  E 12.31      L 4.03     B 1.62
        //  T  9.59      D 3.65     G 1.61
        //  A  8.05      C 3.20     V 0.93
        //  O  7.94      U 3.10     K 0.52
        //  N  7.19      P 2.29     Q 0.20
        //  I  7.18      F 2.28     X 0.20
        //  S  6.59      M 2.25     J 0.10
        //  R  6.03      W 2.03     Z 0.09
        //  H  5.14      Y 1.88      

        
string[] _letterValue 
        
{
            
"e""l""b""t""d""g""a""c""v",
            
"o""u""k""n""p""q""i""f""x",
            
"s""m""j""r""w""z""h""y"
        
};
        int
[] _letterFreq 
        
{
            
123140316295936516180532093
            
794310527192292071822820,
            
659225106032039514188
        
};
        int 
lastEndRange 0;
        
//Recorriendo los arrays rellenados con esos datos
        //guardamos una colección de objetos Letra llamada AllLetters, 
        //asignando a cada Letra su rango, valor y tipo
        
for (int 0i < _letterFreq.Lengthi++)
        {
            Letter nuLetter 
= new Letter();
            
nuLetter.iniRange lastEndRange;
            
nuLetter.endRange lastEndRange + _letterFreq[i];
            
lastEndRange nuLetter.endRange + 1;
            
nuLetter.Value _letterValue[i];
            
nuLetter.Type IsAVowel(_letterValue[i]) ? 
                LetterType.Vowel : LetterType.Consonant
;
            
AllLetters.Add(nuLetter);
        
}
    }
     
    
/// <summary>
    /// Ver el comentario de LoadLetters
    /// </summary>
    
static void LoadDigrams()
    {
        
// http://www.cs.bris.ac.uk/Teaching/Resources/COMS30124/Labs/freq.html
        // Digramas ingleses y su porcentaje de frecuencia de uso
        //  TH  3.15   TO  1.11   SA  0.75   MA  0.56 
        //  HE  2.51   NT  1.10   HI  0.72   TA  0.56
        //  AN  1.72   ED  1.07   LE  0.72   CE  0.55
        //  IN  1.69   IS  1.06   SO  0.71   IC  0.55
        //  ER  1.54   AR  1.01   AS  0.67   LL  0.55
        //  RE  1.48   OU  0.96   NO  0.65   NA  0.54
        //  ES  1.45   TE  0.94   NE  0.64   RO  0.54
        //  ON  1.45   OF  0.94   EC  0.64   OT  0.53
        //  EA  1.31   IT  0.88   IO  0.63   TT  0.53
        //  TI  1.28   HA  0.84   RT  0.63   VE  0.53
        //  AT  1.24   SE  0.84   CO  0.59   NS  0.51
        //  ST  1.21   ET  0.80   BE  0.58   UR  0.49
        //  EN  1.20   AL  0.77   DI  0.57   ME  0.48
        //  ND  1.18   RI  0.77   LI  0.57   WH  0.48
        //  OR  1.13   NG  0.75   RA  0.57   LY  0.47 

        
string[] _digramValue 
        
{   
            
"th""to""sa""ma""he""nt""hi""ta",
            
"an""ed""le""ce""in""is""so""ic",
            
"er""ar""as""ll""re""ou""no""na",
            
"es""te""ne""ro""on""of""ec""ot",
            
"ea""it""io""tt""ti""ha""rt""ve",
            
"at""se""co""ns""st""et""be""ur",
            
"en""al""di""me""nd""ri""li""wh",
            
"or""ng""ra""ly"
        
};
        int
[] _digramFreq 
        
{
            
31511175562511107256,
            
17210772551691067155
            
1541016755148966554
            
145946454145946453
            
131886353128846353
            
124845951121805849
            
120775748118775748
            
113755747
        
};
        int 
lastEndRange 0;
        
//Recorremos los arrays de datos para rellenar
        //la colección de objetos Digram AllDigrams
        //asignándole a cada uno su rango,
        //valor y tipo de digrama
        
for (int 0i < _digramFreq.Lengthi++)
        {
            Digram nuDigram 
= new Digram();
            
nuDigram.iniRange lastEndRange;
            
nuDigram.endRange lastEndRange + 
                _digramFreq[i]
;
            
lastEndRange nuDigram.endRange + 1;
            
nuDigram.Value _digramValue[i];
            if 
(IsAVowel(_digramValue[i].Substring(01)) && 
                IsAVowel(_digramValue[i].Substring(
11)))
                    nuDigram.Type 
DigramType.TwoVowels;
            else if 
(!IsAVowel(_digramValue[i].Substring(01)) && 
                IsAVowel(_digramValue[i].Substring(
11)))
                    nuDigram.Type 
DigramType.ConsonantAndVowel;
            else if 
(IsAVowel(_digramValue[i].Substring(01)) && 
                !IsAVowel(_digramValue[i].Substring(
11)))
                    nuDigram.Type 
DigramType.VowelAndConsonant;
            else 
nuDigram.Type DigramType.TwoConsonants;
            
AllDigrams.Add(nuDigram);
        
}
    }
#endregion
}

Colorized by: CarlosAg.CodeColorizer

Actualización 13/01/2007.- Traducidos los comentarios del código fuente al español. Éste es un ejemplo de los apellidos que se generan:
  • Andaso
  • Estecon
  • Ovloso
  • Eleasawh
  • Athene
  • Tertore
  • Setolyiis
  • Face
  • Lirsuco
  • Rofsender

Comprimiendo y descomprimiendo ficheros ZIP

Published 78 weeks, 1 day ago
Thu Jan 04 2007
Para comprimir y descomprimir ficheros en formato ZIP tenemos una herramienta muy valiosa a nuestra disposición: la excelente librería SharpZipLib, creada por IC#Code, el mismo grupo de desarrolladores que nos han proporcionado el excelente IDE Open Source para C# SharpDevelop, del que ya os he hablado otras veces.

Lo primero que tenemos que hacer, por lo tanto, es descargar dicha librería a nuestro sistema. Una vez hecho esto, debemos incluirla como referencia en el proyecto de Visual Studio en el que vayamos a usarla. Si creemos que vamos a usarla con relativa frecuencia, lo más cómodo es instalarla en el GAC. Para ello, abrimos la Ventana de comandos de Visual Studio 2005, que encontraremos bajo Herramientas del grupo del menú de Inicio Visual Studio 2005. Cuando lo ejecutamos, se abre una consola que deberemos dirigir a la ruta donde hayamos descargado la librería ICSharpCode.SharpZipLib.dll (por comodidad, yo la tengo en una carpeta propia en C:\Archivos de Programa) y una vez situados ahí escribimos el siguiente comando:

gacutil /i ICSharpCode.SharpZipLib.dll

De esta manera añadimos la librería a la caché global de ensamblados para Visual Studio.

Tanto si añadimos la librería al GAC como si no, debemos referenciarla en nuestro proyecto. Para ello, ya sabéis, una vez creado nuestro proyecto (de consola, Windows o Web) elegimos el menú Proyecto, Añadir Referencia y elegimos la librería en el cuadro de diálogo que nos aparece: en la solapa llamada .NET si hemos incluido la librería en el GAC, o podemos buscarla mediante la solapa Examinar. Una vez incluida la referencia, incluimos la línea

using ICSharpCode.SharpZipLib.Zip;

en nuestro código y ya estamos listos.
Una última aclaración antes de meternos en faena: para hacer más simple este artículo he decidido que los ejemplos de código incluidos funcionen en la consola y he dado por supuestas varias cosas, como rutas y nombres de ficheros. Si quieres que el código fuente te funcione no te limites a cortarlo y pegarlo, debes adaptarlo a las rutas y nombres de ficheros existentes en tu sistema.

Listar Contenido de Ficheros

Vamos a ver primero cómo listar el contenido de un fichero ZIP. Para