Archive for 'Difusión' category

Si has leído The Pragmatic Programmer probablemente te acuerdes de dos consejos básicos:

  • Keep your knowledge in Plain Text
  • Always Use Source Code Control

El texto plano nunca se volverá obsoleto. Añadimos un software de control de versiones y estamos en una situación win-win.

Te sentirás como Marty McFly con su DeLorean DMC-12, podrás viajar en el tiempo comprobando como cambiaron tus archivos. Qué líneas fueron modificadas, en qué archivos, qué líneas se añadieron, cuáles se eliminaron… como mucho estarás a un par de comandos de contestar todas esas preguntas.

Cuando le cojas gusto querrás tener bajo control de versiones tus archivos de configuración, tus scripts caseros… incluso se te puede ocurrir tener bajo revisión tu instalación completa de Windows.

Aunque algunas versiones modernas de herramientas de comparación de archivos soportan archivos binarios lo habitual es realizar una comparación línea por línea de los archivos. Por lo tanto cuando comparemos dos versiones de archivos binarios podremos saber si han cambiado o no lo han hecho, pero no podremos saber que es lo que exactamente ha cambiado.

Durante las últimas semanas estoy trabajando con archivos de Microsoft Word 2007, no precisamente por gusto, sino porque era estrictamente necesario trabajar con este formato. Yo hace tiempo que le cogí gusto a eso de poner todo bajo control de versiones.
Desde hace unos cuantos meses, y siempre que puedo, suelo elegir Git como software de control de versiones. Así que uso Git para llevar un control de cambios sobre todos esos documentos de Word 2007.

Como ya hemos comentado el problema con ese tipo de archivos (binarios) es que no podemos hacer una comparación línea a línea entre dos versiones. De modo que cuando ejecutemos git diff obtendremos un mensaje diciéndonos que son archivos binarios.

Cómo podemos solucionar esto y poder ver qué se modificó en cada archivo de Word?
Los archivos de Word 2007 en realidad son archivos ZIP que contienen documentos XML y otros archivos de datos. Podríamos reinventar la rueda otra vez hoy, pero como ya existen herramientas que extraen el texto (plano) de documentos Office Open XML (formato de Word 2007) nos limitaremos a usar alguna de ellas. Una búsqueda rápida para docx to text y tenemos un script en perl que hará el trabajo sucio: extraer el texto de nuestros documentos Word 2007.

A continuación voy a realizar un ejemplo en el que se mostrará lo que sucedería al tratar de comparar dos versiones de un archivo de Word 2007 y como configurar nuestro Git y nuestro repositorio para poder automatizar el proceso de compararlas usando el texto plano que produce el script en perl.

Mi versión actual de Git es la 1.6.3.2.

iMac:~ rochoa$ git --version
git version 1.6.3.2

Creamos un directorio nuevo y nos cambiamos a su ubicación, iniciamos un nuevo repositorio y comprobamos su estado:

iMac:~ rochoa$ mkdir -p ~/git/example && cd ~/git/example
iMac:example rochoa$ git init
Initialized empty Git repository in /Users/rochoa/git/example/.git/
iMac:example rochoa$ git status
# On branch master
#
# Initial commit
#
nothing to commit (create/copy files and use "git add" to track)

Creamos un documento Word en este directorio, ejemplo:

commit_0

Comprobamos el estado del repositorio, añadimos el fichero a nuestro repositorio, hacemos el commit inicial y volvemos a comprobar el estado del repositorio.

iMac:example rochoa$ git status
# On branch master
#
# Initial commit
#
# Untracked files:
#   (use "git add ..." to include in what will be committed)
#
#	document.docx
nothing added to commit but untracked files present (use "git add" to track)
iMac:example rochoa$ git add .
iMac:example rochoa$ git commit -m "initial commit"
[master (root-commit) 0ffe9fe] initial commit
 1 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 document.docx
iMac:example rochoa$ git status
# On branch master
nothing to commit (working directory clean)

Modificamos nuestro documento, por ejemplo:

commit_1

Comprobamos el estado del repositorio y hacemos commit. Lo mensajes tendían a ser largos y detallados con el fin de tener toda la información posible en el registro.

iMac:example rochoa$ git status
# On branch master
# Changed but not updated:
#   (use "git add ..." to update what will be committed)
#   (use "git checkout -- ..." to discard changes in working directory)
#
#	modified:   document.docx
#
no changes added to commit (use "git add" and/or "git commit -a")
iMac:example rochoa$ git commit -am "removing info about how easy&fast brandching and merging are, added projects using git as dcvs"
[master 455130a] removing info about how easy&fast brandching and merging are, added projects using git as dcvs
 1 files changed, 0 insertions(+), 0 deletions(-)

Se usaban mensajes largos y detallados porque a la hora de comparar versiones se obtenía el mensaje de aviso de comparación de archivos binarios.

iMac:example rochoa$ git diff HEAD^

diff --git a/document.docx b/document.docx
index b01c1ac..0761a57 100644
Binary files a/document.docx and b/document.docx differ

Por lo que la única opción era hacer uso del log para saber que se había modificado.

iMac:example rochoa$ git log
commit 455130a12ae51f264c2528c1d42f306e41dc382f
Author: Raul Ochoa
Date:   Wed Jun 10 22:27:14 2009 +0200

    removing info about how easy&fast brandching and merging are, added projects using git as dcvs

commit 0ffe9fedb2828678a4612ebdb1625fb4f515f94a
Author: Raul Ochoa
Date:   Wed Jun 10 22:19:42 2009 +0200

    initial commit

Cómo poder comparar el texto de nuestros documentos Word?

Lo primero que necesitamos es tener el script perl en nuestro PATH.

iMac:~ rochoa$ docx2txt.pl 

Usage:	/Users/rochoa/bin/docx2txt.pl  [outfile.txt|-]

	Use '-' as the outfile name to dump the text on STDOUT.
	Output is saved in infile.txt if second argument is omitted.

A continuación crearemos otro ejecutable, por ejemplo docx2plain, que deberá estar también en nuestro PATH y que tendrá el siguiente contenido:

#!/bin/bash
docx2txt.pl $1 -

Básicamente pasará como argumento al script perl un documento Word y redigirá la salida a STDOUT.

Podemos probar su funcionamiento con nuestro documento:

iMac:example rochoa$ docx2plain document.docx
Git

Git is a free & open source, distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
Every Git clone is a full-fledged repository with complete history and full revision tracking capabilities, not dependent on network access or a central server.
Several high-profile software projects now use Git for revision control, most notably the Linux kernel, Perl, GNOME, Samba, X.org Server, Qt, One Laptop per Child (OLPC) core development, VLC, Wine, Ruby on Rails, and the Android mobile platform.

Lo siguiente será modificar nuestro archivo de configuración de git: ~/.gitconfig
En el que añadiremos las siguientes dos líneas:

[diff "docx"]
    textconv = docx2plain

Con esto conseguimos indicarle a git que tiene una opción para convertir determinados objetos de lo repositorios en texto en caso de que se use el mecanismo de diff “docx”.

Ahora es necesario añadir un fichero nuevo a nuestro repositorio: .gitattributes, al que le añadiremos la siguiente línea:

*.docx diff=docx

Con esto conseguimos forzar el uso del mecanismo de diff “docx” cuando se encuentre ficheros que cumplan con el patrón de nombre indicado.

Si todo está correctamente configurado y los scripts funcionan deberíamos poder comparar las diferentes versiones de nuestro documento como si de texto plano se tratase:

iMac:example rochoa$ git diff HEAD^
diff --git a/document.docx b/document.docx
index b01c1ac..0761a57 100644
--- a/document.docx
+++ b/document.docx
@@ -1,4 +1,5 @@
 Git

 Git is a free & open source, distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
-Every Git clone is a full-fledged repository with complete history and full revision tracking capabilities, not dependent on network access or a central server. Branching and merging are fast and easy to do.
+Every Git clone is a full-fledged repository with complete history and full revision tracking capabilities, not dependent on network access or a central server.
+Several high-profile software projects now use Git for revision control, most notably the Linux kernel, Perl, GNOME, Samba, X.org Server, Qt, One Laptop per Child (OLPC) core development, VLC, Wine, Ruby on Rails, and the Android

Lo mejor de todo esto es que no es necesario preocuparse de generar archivos intermedios, Git se encarga de todo (con la inestimable ayuda, en este caso, de nuestro script perl).

En realidad esta técnica es válida para cualquier tipo de documento que permita extraer texto (plano) de su contenido, simplemente tendrías que usar un ejecutable adecuado para cada tipo de archivo binario: PDF, Word <2007 …

PD: Antes de que me preguntéis si conozco la herramienta de control de cambios de Microsoft Word, os diré que sí que la conozco.

007_hadoop

El jueves 23 de abril doy una charla sobre Hadoop en la Universidad de La Rioja.

A partir de las 19 horas en el aula 311 del edificio Vives y durante unos 90 minutos se presentará el proyecto, los conceptos detrás de él y sus posibles usos. Además se realizará una pequeña demostración.

Puzzle, sumando y restando

Vamos a echar uno rápido:

public class SumarRestar {

	public static void main(String[] args) {

		int i = 0;

		i = --i-i+++i---i+++i-i--;

		System.out.println(i);

	}

}

Quién me dice qué saldrá por pantalla?

ATENCIÓN: si soportas cualquier tipo de ideología/religión a la que el siguiente texto le pueda resultar molesto, reemplaza, a tu gusto, tantos términos como desees hasta que te encuentres ante un contenido coherente con tus principios/creencias. No pretendo molestar a nadie.

RMS fanboys: Java ya no es malo.

AVISO: probablemente lo que voy a contar no lo haya inventado yo. No soy tan listo.

Qué delicado es el mundo de las relaciones. Lewis era un apuesto y joven campeón de Fórmula 1, vivía feliz y enamorado de la que entonces era su novia, Nicole, una no-famosa cantante de un no-famoso grupo pop (algo bastante común entre los campeones de F1 de aquellos años). Lewis tenía un problema: había tres chicas que estaban locamente enamoradas de él, ellas eran Katie, Price y Jordan, unas jóvenes muy atentas: las observadoras se hacían llamar. Tal era su obsesión por el joven inglés, que hicieron lo imposible hasta conseguir el número de teléfono de Lewis para poder llamarle y saber, en todo momento, si su estado con Nicole había cambiado.

Al principio recibía una llamada al día de Katie, Price y Jordan. Pero según pasaba el tiempo la cantidad de llamadas que tenía que contestar diariamente era mayor, aquello no parecía estar bien: su estado seguía siendo el mismo pero como buen caballero inglés que él era no se veía capaz de dejar de contestar a aquellas llamadas. Por otra parte a Nicole no le hacía mucha gracia lo de aquellas tres arpías.

Lewis era un tipo listo, un tipo con cabeza (que no con cabezón). Aquello tenía que cambiar, él no podía despreciar a aquellas maravillosas mujeres, además si el día de mañana su estado cambiaba quería que ellas fuesen las primeras en saberlo, pero por otra parte no podía soportar aquella ingente cantidad de llamadas. Entonces decidió que lo mejor era llegar a un acuerdo con Katie, Price y Jordan para, sin tener que ser ellas las que llamasen continuamente, poder avisarlas cuando su estado cambiase: era tan sencillo como tener su número y mandar un mensaje cuando su estado cambiase. Las ventajas eran claras: las observadoras podrían enterarse, Lewis no tendría que dejar de atender a Nicole para atender a llamadas intranscendentes, llamadas que iban a recibir la misma respuesta.

Katie, Price y Jordan estaban de acuerdo, pero a cambio le impusieron a Lewis una condición: podrían avisarle en cualquier momento para decirle que ya no estaban interesadas en él y que, por tanto, no hacía falta que fuesen notificadas de su cambio de estado. Lewis, chico listo, estaba de acuerdo, pero como no le gustaba desaprovechar ninguna posibilidad decidió que además cualquier chica debería poder suscribirse a su sistema de notificación.

Al principio todas seguían interesadas, Lewis era un sujeto deseado e interesaba conocer sus cambios de estado. Lewis se casó y todas las observadoras fueron avisadas, entonces Jordan decidió que ya no estaba interesada en Lewis y así se lo hizo saber. El grupo de las famosas observadoras ahora sólo estaba formado por Katie y Price.

Y era cuando ella, Nicole, tenía que decirle a Lewis algo así: I don’t need a man to make it happen / I get off being free / I don’t need a man to make me feel good / I get off doing my thing / I don’t need a ring around my finger / To make me feel complete / So let me break it down / I can get off when you ain’t around / Oh!. Su estado había cambiado, nadie le iba a llamar porque así lo había acordado con las observadoras, pero Lewis era un chico listo y como había acordado un método para avisar a las observadoras sólo tenía que ejecutarlo para que Katie y Price supiesen que su estado había cambiado. Katie y Price podían llamar a Lewis para conocer su estado puesto que sabían que este había cambiado.

Lo más increíble de esta historia no es que esté casi-basada en personajes reales, es el hecho de que todo esto sea un patrón de diseño: observer, en este caso siguiendo un protocolo de pull, sólo se notifica que el estado ha cambiado y los suscriptores se tiene que encargar de obtener el nuevo estado. Si se hubiese escogido un protocolo de push el estado podría haber sido pasado a través del método de aviso.

El acuerdo:

public interface SujetoDeseado {
	public void interesarse(Observadora o);
	public void desinteresarse(Observadora o);
	public void avisarObservadoras();
}
public interface Observadora {
	public void avisar();
}

El bueno y afable de Lewis:

import java.util.ArrayList;

public class Lewis implements SujetoDeseado {

	public enum Estado {
		SOLTERO, ENNOVIADO, CASADO
	};

	private ArrayList<Observadora> observadoras;
	private Estado estado;

	public Lewis() {
		this.observadoras = new ArrayList<Observadora>();
		this.estado = Estado.ENNOVIADO;
	}

	@Override
	public void avisarObservadoras() {
		for (Observadora o : this.observadoras) {
			o.avisar();
		}

	}

	@Override
	public void desinteresarse(Observadora o) {
		this.observadoras.remove(o);
	}

	@Override
	public void interesarse(Observadora o) {
		this.observadoras.add(o);
	}

	public Estado getEstado() {
		return this.estado;
	}

	public void estadoCambiado() {
		this.avisarObservadoras();
	}

	public void setEstado(Lewis.Estado estado) {
		this.estado = estado;
		this.estadoCambiado();
	}

}

Katie:

public class Katie implements Observadora {

	private SujetoDeseado lewis;

	public Katie(SujetoDeseado lewis) {
		this.lewis = lewis;
		lewis.interesarse(this);
	}

	public void loDelAmor() {
		System.out
				.println("Katie: A mi si que me vas a poder quitar todo!");
	}

	@Override
	public void avisar() {
		if (lewis instanceof Lewis) {
			Lewis l = (Lewis) lewis;
			switch (l.getEstado()) {
			case CASADO:
				this.esoNoVaADurar();
				break;
			case ENNOVIADO:
				this.loDeHacerseLaIndiferente();
				break;
			case SOLTERO:
				this.loDelAmor();
			}
		}
	}

	private void loDeHacerseLaIndiferente() {
		System.out.println("Katie: me da igual, sé que me quiere a mi.");
	}

	private void esoNoVaADurar() {
		System.out.println("Katie: esa relación no tiene futuro.");
	}

}

Price:

public class Price implements Observadora {

	private SujetoDeseado lewis;

	public Price(SujetoDeseado lewis) {
		this.lewis = lewis;
		lewis.interesarse(this);
	}

	public void loDelAmor() {
		System.out.println("Price: Lewis que voy sin bragafaja!");
	}

	@Override
	public void avisar() {
		if (lewis instanceof Lewis) {
			Lewis l = (Lewis) lewis;
			switch (l.getEstado()) {
			case CASADO:
				this.mePonenLosCasados();
				break;
			case ENNOVIADO:
				this.loDeTirarLosTrastos();
				break;
			case SOLTERO:
				this.loDelAmor();
			}
		}
	}

	private void mePonenLosCasados() {
		System.out.println("Price: Ains como me ponen los casados!");
	}

	private void loDeTirarLosTrastos() {
		System.out.println("Price: Tu padre podría vivir con nosotros.");
	}

}

Y Jordan:

public class Jordan implements Observadora {

	private SujetoDeseado lewis;

	public Jordan(SujetoDeseado lewis) {
		this.lewis = lewis;
		lewis.interesarse(this);
	}

	public void loDelAmor() {
		System.out.println("Jordan: Vente pacá' y mira que escotazo!");
	}

	@Override
	public void avisar() {
		if (lewis instanceof Lewis) {
			Lewis l = (Lewis) lewis;
			switch (l.getEstado()) {
			case CASADO:
				this.loDeBuscarseOtro();
				break;
			case ENNOVIADO:
				this.loDeTirarLosTrastos();
				break;
			case SOLTERO:
				this.loDelAmor();
			}

		}
	}

	private void loDeTirarLosTrastos() {
		System.out.println("Jordan: Ains que guapeton es mi Hamilton!");
	}

	private void loDeBuscarseOtro() {
		System.out.println("Jordan: Que le den, yo me voy con Eddie Irvine");
	}

}

La historia de amores y desamores:

public class Main {
	public static void main(String[] args) {
		// Katie, Price y Jordan llegan a un acuerdo para que Lewis
		// pueda mandar un aviso cuando su estado cambie.
		Lewis lewis = new Lewis();
		// Todas decicen suscribirse... Lewis es tan MONO.
		new Katie(lewis);
		new Price(lewis);
		Jordan jordan = new Jordan(lewis);
		// Lo primero que hace Lewis es avisar que está ennoviado con Nicole.
		lewis.setEstado(Lewis.Estado.ENNOVIADO);
		// Luego decide casarse, su estado cambia por tanto avisa.
		// Jordan ya le avisó que el día que su estado fuese el de casado se
		// desinteresaría.
		lewis.setEstado(Lewis.Estado.CASADO);
		// Nicole le canta eso de I don't need a man...
		// Pero ya sólo Katie y Price siguen interesadas en él, así que son las
		// únicas que se enteran de su nuevo estado
		lewis.desinteresarse(jordan);
		lewis.setEstado(Lewis.Estado.SOLTERO);
	}
}

Y el resultado de toda esta historia:

Katie: me da igual, sé que me quiere a mi.
Price: Tu padre podría vivir con nosotros.
Jordan: Ains que guapeton es mi Hamilton!
Katie: esa relación no tiene futuro.
Price: Ains como me ponen los casados!
Jordan: Que le den, yo me voy con Eddie Irvine
Katie: A mi si que me vas a poder quitar todo!
Price: Lewis que voy sin bragafaja!

Te puedes meter con la implementación todo lo que quieras, sobre todo con la visibilidad de algunos de los métodos. O por el hecho de extender java.util.Observable, pero esto ya es más discutible :-).

Y sí, probablemente debería haber aprovechado el tiempo en otra cosa, pero los domingos no suelen ser los mejores días para trabajar.

Puzzles en Java

A raíz de mi irónica entrada sobre PHP el jrande de Diego me recomendó ver Advanced Topics in Programming Languages: Java Puzzlers, un ameno tech talk presentado por Joshua Bloch (os debería sonar del JavaDoc) y William Pugh.

Me gustó Histogram Mystery (43min 40s), así que he preparado una versión simplificada y castellanizada. Dos cervezas para el primero que diga cuál es el resultado y además lo justifique (sin haber visto el vídeo, claro). Y cuidado con las posibles respuestas… por aquello de la rima fácil.

public class AbsolutVodkaPuzzle {
	public static void main(String[] args) {
		String frase = "Al anochecer llamaron al celular";
		String [] palabras = frase.split(" ");
		int i = 0;
		for (String palabra1 : palabras) {
			for (String palabra2 : palabras) {
				String dosPalabras = palabra1 + palabra2;
				int valorAbsolutoHash = Math.abs(dosPalabras.hashCode());
				if (valorAbsolutoHash >= 0) {
					i++;
				}
			}
		}
		System.out.println('1' + i);
	}
}

ExtraLap™: Josh Bloch es autor de Effective Java y coautor de Java Puzzlers y Java Concurrency in Practice (de este último todavía no he podido disfrutar). Y además es una de las personas que consigue que [ames|odies] Java.



About Raúl

Raúl Ochoa, a spaniard working for Tuenti in Madrid, Spain. More about me.

Subscribe to the feed

If you want to receive a notification when I update the website, you only have to add the feed to your reader, or submit your email address and I'll let you know.

Categories