miércoles, febrero 20, 2008

Observaciones Cpp

Observaciones Cpp


Puede que lo que voy a contar sea conocido en gran medida, pero seré breve y rápido.

Si alguien quiere repasar o profundizar en algún punto, fenomenal.

Los ejemplos no se han compilado e intencionadamente omito cosas que pueden hacer que no compilen (seguro que es obvio y no aporta nada escribirlo lo que falta)




Herencia
En c++ es normal hacer muchas cosas sin nada de herencia
No conviene abusar de la herencia
Algunos entendidos de c++ dicen que no se debe utilizar la herencia.
Excepto en casos que deben estar muy justificados
La herencia se utiliza con varios fines
Clasificación (es un)
Polimorfismo en tiempo de ejecución
Extender algo que ya existe



Constructor
Curiosa notación
// C++
class A {
int val;

A(int _val) : val(_val) {}

};

// Java
class A {
int val;

A(int _val)
{
val = _val;
}

};
¿Porqué en c++ se utiliza esa notación con ':'?
Son muy puristas
Les preocupa mucho el rendimiento

En el ejemplo anterior no hay una diferencia muy relevante.

En java, cuando se instancia una clase (se crea un objeto), la memoria que ocupará está rellenada con ceros. De esta forma todo se queda inicializado con los valores por defecto (quizá es por razones de seguridad).

Si luego le das un valor inicial, estás inicializando dos veces, una para nada.
Cuando se inicializan muchos objetos así, puede tener impacto en el rendimiento.

Pero en C++, se utiliza esta notación, sobre todo, por ser puristas y claros.

Un ejemplo más largo...

// C++
class A {
int entero;

A(int _entero) : entero(_entero) {}

};


class B {
A val; // #1

B(A valorInic) // esto no compila
{
val = valorInic;
}

};

No compila porque primero se crea val de tipo A con el contructor vacío (linea #1) y luego se intenta copiar el valor de valorInic en val.
Pero A no tiene constructor vacío.

En C++ el código del constructor, se ejecuta siempre, después de que se haya construído el objeto (en Java hay excepciones poco claras en la sintáxis)


Otro tipo de ejemplo...
// C++
class Padre {
int val;

Padre(int _val) : val(_val) {}

};


class Hija : public Padre {

Hija(int _val) // esto no compila
{
val = _val;
}

};

class Hija2 : public Padre {

Hija2(int _val) : Padre(_val) {} // ahora sí

};

Hija tampoco compila porque la clase padre no tiene constructor vacío.
Con la notación ':' se le dice que tiene que hacer en la construcción, no después






Destructores virtuales
Los destructores de C++ sólo son virtuales si se indica explícitamente
Que no sean virtuales les da más rendimiento, pero en ocasiones es necesario que sean virtuales

Ejemplo:
class Fruta {

int* punteroInt;

Fruta() : punteroInt(new int) {};

~Fruta() { delete punteroInt; };

};

class Platano : public Fruta {

int* punteroIntDelPlatano;

Platano() : punteroIntDelPlatano(new int) {};

~Platano() { delete punteroIntDelPlatano; };

};

...

Fruta* f = new Platano();

delete f;
En este mega programa tenemos una fuga de memoria (memoria sin liberar)
El tema es que f es de tipo de Fruta aunque señale a un plátano (como un plátano es una fruta...)
El delete f, llama al destructor de Fruta, con lo que se borra de la memoria su punteroInt. NUNCA SE LLAMA AL DESTRUCTOR del plátano (y este no tiene oportunidad de liberar su puntero, pobre....)
Una opción es hacer... delete dynamic_cast(f); (pero hay que estar un poco enfermo
Es mala opción porque no siempre sabes que f tiene un plátano, podría ser una fresa, y probar con todas las frutas es mala solución.
Opción 2. Hacer virtual el destructor del padre. SÍÍÍÍ
Opción 3. Pensárselo dos veces antes de utilizar la herencia y polimorfismo en tiempo de ejecución.
IMPORTANTE. Este problema no afecta sólo a la memoria, afecta a todo recurso "no local". Por ejemplo, un semáforo, región crítica, conexión bbdd, etc...
LA OPCIÓN CORRECTA... RAII
Tratar de no liberar explícitamente recursos (de ningun tipo)
Esto en el caso de la memoria, se puede hacer con un CountPtr
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization)
Conviene saberlo aunque es poco probable que os suceda. Un escenario posible es con la vcl (haciendo algún componente)





Operador asignación y constructor de copia
C++ es muy amable y propone siempre que puede constructores de copia y operadores de asignación.
Pero no siempre los que propone sirven y hacen lo que queremos.
Es muy importante, siempre preguntarse si el que crea nos sirve o no
Anotar con un comentario que se ha verificado es saludable
OJO con clases con recursos no locales, habitualmente memoria en forma de punteros o referencias
Una curiosidad importante
class Pr {

Pr(int i)...

};

Pr prueba = Pr(4);
Aquí parece que...
Se instancia prueba de tipo Pr con constructor vacío
Luego se instancia Pr con valor 4
Luego se llama al operador de asignación para copiar el segundo objeto en el primero

PUES NO
Esa línea es totalmente equivalente a la siguiente (más clara en lo que hace)
Pr prueba(4);



Clases índice en mapas
std::map
Muy útil para almacenar datos indexados por lo que queramos de forma segura (verificación en tiempo de compilación) y eficiente
En un mapa la clase índice debe tener definida una relación de orden extricta con el operador <. Vamos, que si definimos el operador < style="font-weight: bold;">iteradores y end()
Por ejemplo, en un mapa llamamos al método find(xxx)
miMapa.find(esto)
SIEMPRE comprobar que no vale miMapa.end() (que significa que no lo ha encontrado)
En la próxima especificación de C++ esto será mejor
En Scala, en tiempo de compilación se verifica que no te olvidas
Si no se hace esta verificación, el programa explotará en algunas ocasiones





Punteros y referencias
No utilizar punteros crudos ni aritmética de punteros
Las referencias son punteros que no lo parecen (indirección implícita) y que siempre tienen valor (siempre están inicializados) y no tienen aritmética de punteros
Punteros nunca, referencias sí, en muchos casos
Envíe "aunque parezca increíble estoy leyendo esto" al 555 55 55, participará en el sorteo de un concepto
Los punteros a punteros y punteros a referencias NOOOOOOOO, caca, caca




Fugas de memoria
Dicen que C y C++ padecen de esto, pero... no parece razonable para el caso de C++
Para los casos raros en los que es necesario un puntero (memoria de montículo o por polimorfismo en tiempo de ejecución), utiliza un CountPtr
El problema del CountPtr está en las referencias circulares, pero no es tan fácil diseñar algo con referencias circulares en C++, especialmente si no se abusan de los punteros.
Con muy poco cuidado, eso no pasará nunca




Multimétodos
C++ no tiene
Está de moda, pero no hay consenso y lo discuten mucho
Scala, que ha metido de todo, no los tiene. El autor inicialmente dijo que no se acordó, pero después no lo puso porque decía era mejor sin.
Hay discusiones sobre esto en internet
A pesar de ser un concepto sencillo, tarde bastante en enterarme
// sin multimétodos

class Grafo {
virtual Distancia(Punto punto)=0;
}


class Punto : public Grafo
{

Distancia (Punto punto);
}

class Linea : public Grafo
{

Distancia (Punto punto);
}


Grafo g1 = new Punto(100,200);
Grafo g2 = new Punto(200, 600);

g1.Distancia(Punto(120, 400));

Grafo g3(100, 250, 30);

g3.Distancia(Punto(120, 400));



// con multimétodos

class Grafo {
...
}


class Punto : public Grafo
{
...
}

class Linea : public Grafo
{
...
}


Distancia(Punto p1, Punto p2) ...
Distancia(Linea l1, Punto p1) ...
Distancia(Linea l1, Linea l2) ...


Grafo g1 = new Punto(100,200);
Grafo g2 = new Punto(200, 600);
Grafo g3(100, 250, 30);

Distancia(g1, g2);
Distancia(g3, g2);

Sin multimétodos, la llamada a Distancia del un objeto Grafo, se pasa en tiempo de ejecución al hijo que corresponda, un punto o una línea (polimorfismo en tiempo de ejecución, una de las claves fundamentales de la OOP)
Pero el segundo objeto sobre el que se realiza el cálculo (el parámetro), no puede utilizar el mismo truco
Con multimétodos, a pesar de que llamamos a Distancia con dos parámetros de tipo Grafo, y no existe una Distancia definida para dos Grafos, en tiempo de ejecución, verifica que uno de los parámetros concretamente es un punto y el otro una línea y llama a esa Distancia concreta (o la que corresponda)
Se parece al polimorfismo en tiempo de ejecución, pero no sólo para uno, sino para varios
Lo dicho, que hay una discusión sobre el tema que se puede ver en internet
No está en C++ ni estará en la próxima especificación



Herencia múltiple de implementación
No utilizar
Es cierto que en algunos casos puede ser muy interesante, pero es muy delicado
Ahora está más de moda (y parece mejor) los mixins




Conversión implícita
Es un recurso potente que debe utilizarse con cuidado
Se puede conseguir en varias direcciones
Un constructor con un sólo parámetro del tipo a convertir
Operadores en la clase que se convierte
Podéis ver un ejemplo en la clase Variant de jleTibco
En general no utilizar




NUNCA...
NUNCA Utilizaré aritmética de punteros
NUNCA Utilizaré punteros crudos
NUNCA Arrays al estilo C (que en el fondo son punteros con aritmética de punteros)
En su lugar std::vector
NUNCA Herencia múltiple de implementación
NUNCA Abusaré de la herencia
NUNCA Utilizaré dynamic_cast, const_cast, ... (RTTI)
NUNCA Utilizaré reinterpret_cast (nunca jamás)
NUNCA haré un casting al estilo C (nunca jamás)
NUNCA Haré piezas que requieran liberación explícita de recursos
NUNCA Haré programas con muchas hebras
NUNCA Utilizaré mecanismos de concurrencia de bajo nivel (regiones críticas, semáforos, monitores, etc...)
NUNCA utilizaré "using namespace"
NUNCA intentaré utilizar namespaces anidados
NUNCA diré en la definición de una función o método las excepciones que puede lanzar
NUNCA pondré un nombre a un namespace superior a 3 letras
NUNCA utilizaré macros
NUNCA pensaré que C++ se parece a C
NUNCA manejaré los string de C ni la entrada salida de C, ni lista de parámetros variables (C otra vez)
NUNCA utilizar malloc ni semejantes
NUNCA Pondré degradados de colores ;-P
(consejos de un pecador)

domingo, febrero 17, 2008

programación imperativa vs declarativa

Programación imperativa vs declarativa


Yo no soy expecialista en programación declarativa.

Una visión global y breve...



Conceptos breves antes de empezar...

La característica fundamental de la programación declarativa es que no tiene asignación destructiva. Es decir, que una variable no puede cambiar su valor.

Esto provoca que el orden de ejecución no tiene porque ser secuencial.

Los bucles hay que reemplazarlos por funciones recursivas.






1.---------------------------------------------------------------

Existen problemas que son naturalmente imperativos.
Un programa se descompone en entrada, proceso y salida.
Tanto la entrada como la salida, son acciones imperativas (consola, conexión tcp, fichero, etc..).

La mayoría de lenguajes declarativos, tienen características imperativas para ello (no son declarativos puros)




2.---------------------------------------------------------------

Todos los programadores declarativos conocen la programación imperativa.
Lógicamente, esto es porque la programación imperativa está muchísimo más extendida

Casi todos los programadores declarativos, empezaron con la programación imperativa, y ahora defienden la programación declarativa sobre la imperativa.

Esto podría ser el famoso efecto de las minorías y débiles, que tratan de compensarlo gritando más.
Pero en este caso, lo argumentan con lógica.

Richard Stallman, un genio de la programación, eligió LISP para su proyecto estrella Emacs

c omega es el lenguaje de programación para experimentar novedades para c#.
Inicialmente, este lenguaje se obtenía modificando el compilador de c# (que está escrito en c++ ¿no es tan bueno c#?).
Pero los programadores dijeron que era muy duro andar tocando el compilador c++ y optaron por utilizar un dialecto de scheme que generaba código c#, que luego volvían a compilar.

Aquí hay un tutorial de haskell para programadores imperativos
http://www.haskell.org/~pairwise/intro/intro.html






3.---------------------------------------------------------------

En la programación declarativa es más fácil la verificación de los programas.
En programación imperativa no sólo importa los valores con los que se llaman, también depende el orden de la llamada al método, a los métodos del objeto y casi siempre, a cualquier cosa del programa. Esto provoca una explosión de posibilidades imposible de verificar.





4.---------------------------------------------------------------

Los lenguajes declarativos suelen tener un poder expresivo impresionante.
El caso más radical es Haskell. Probablemente el lenguaje más potente y expresivo.

Quizá para ser capaz de sacarle partido, es necesaria una capacidad especial.





5.---------------------------------------------------------------

Los lenguajes declarativos, especialmente Haskell tienen fama de no ser muy eficientes.

Creo que para ejemplos sencillos, que se pueden optimizar fácilmente en c/c++ (por ejemplo), Haskell puede estar detrás.
Pero para casos reales, grandes y complejos de optimizar, Haskell tiene ventajas importantes.

La evaluación perezosa, la optimización dinámica y que el compilador pueda elegir el orden de ejecución para buscar el más óptimo... seguramente produzcan un código muy eficiente, sin mucha preocupación al programador.
Sin estas herramientas, todos sabemos, que las optimizaciones son la principal fuente de errores en los programas.






6.---------------------------------------------------------------

Los lenguajes declarativos son naturalmente paralelizables

Aquí la diferencia es radical e incomparable.
Un ejemplo muy bueno está en el lenguaje Erlang.

Erlang es un lenguaje declarativo muy, muy sencillo (uno de los más sencillos que he visto en mi vida)
Está orientado a sistemas de ejecución paralela, distribuida, tolerante a fallos, ejecución continua...

Para el SO, crear un proceso es caro, conmutar un proceso es caro.
Es más barato con hebras (para eso se crearon), pero aún así sigue siendo un recurso caro (unos pocos miles es demasiado).

En Erlang, crear un proceso es increíblemente barato, y lo mismo para las conmutaciones, y lo mismo para el paso de mensajes (nada de numeritos, mensajes complejos)

Ejemplo en yaws (yet another web server). Comparación con apache.
http://www.sics.se/~joe/apachevsyaws.html

Esta comparación es salvaje, pero también es comparar dos productos en el punto fuerte de uno de ellos.

En 4000 peticiones simultáneas, Apache casca, demasiados procesos.
Mientas que yaws (erlang) da mejor rendimiento incluso para 80.000 peticiones simultáneas.






7.---------------------------------------------------------------

El futuro es la programación paralela, dicen algunos.

La velocidad de los micros se ha parado, ahora buscan potencia haciéndolos multinúcleo.
Intel dice que prevee meter hasta 100 núcleos.

¿Están los programas preparados para aprovechar estas nuevas configuraciones?
NO
¿Están los lenguajes de programación preparados para ayudar en este nuevo escenario?
Los imperativos no.

Aquí tenéis un artículo fantástico
http://www.gotw.ca/publications/concurrency-ddj.htm




8.---------------------------------------------------------------

La programación declarativa es extrictamente académica??

Seguro que no está tan difundida como la imperativa, pero muchos defienden que no es puramente académica.

Se utilizar en las universidades, por profesores e investigadores, porque probablemente es la mejor opción.

Erlang está funcionando en máquinas muy caras con unas cifras espectaculares.
Ericson está explotando Erlang en máquinas con programas de más de 3 millones de líneas de código, con una tasa de fallo de 31 milisegundos al año
http://www.erlang.se/publications/BYTE2003.html





9.---------------------------------------------------------------

La programación declarativa es mejor para cosas pequeñas.

Erlang ha demostrado que puede funcionar muy bien en proyectos enormes.

Exprogramadores imperativos dicen que la gran ventaja de Haskell es para programas grandes o enormes
http://www.haskell.org/~pairwise/intro/intro.html

Scala recomienda utilizar todo lo posible la programación declarativa para poder eSCALAr al máximo.



10.-------------------------------------------------------------

Gran parte de las novedades de los lenguajes imperativos, o son características declarativas, o son cosas que aparecieron antes en estos lenguajes.



11.-------------------------------------------------------------

Líneas de código, tiempo de desarrollo...
Algunas pruebas dicen que Haskell gana
http://www.haskell.org/papers/NSWC/jfp.ps



12.-------------------------------------------------------------

Un inconveniente con los lenguajes muy expresivos, es que hace difícil la vida a los IDE y los compiladores.
Para un IDE es mucho más difícil funciones como autocompletar.
Para un compilador es más difícil informar con precisión del error.

Ver caso de C++ (seguro que también le sucederá a Scala). Creo que le pasa a Haskell

miércoles, febrero 06, 2008

Matemáticas y beisbol

origen... http://www.historiasdelaciencia.com/?p=317#more-317

¿Qué relación hay entre el béisbol y los números primos? Aunque en el béisbol, al igual que en otros deportes, son muy importantes las estadísticas, en este artículo nos referimos a una relación bastante más curiosa e inesperada con las matemáticas, en particular, con los números primos. Y sobre ello y otras anécdotas versará nuestra historia de hoy.

El 8 de abril de 1974, Hank Aaron bateó un home run (en castellano se puede decir también “jonrón”): el numero 715 de su carrera. La importancia de este home run era que, con él, Aaron rompía la marca histórica que Babe Ruth estableció en 1935 y que estaba precisamente en 714.

Resulta que Carl Pomerance, un matemático que trabajaba en la ciudad de Atlanta, donde Aaron había bateado su home run 715, notó que los factores primos de 714 y 715 satisfacían una propiedad interesante.

Si factorizamos ambos números obtenemos las siguientes descomposiciones:

714 = 2 × 3 × 7 × 17
715 = 5 × 11 × 13

Si nos fijamos en las sumas de ambas factorizaciones tenemos que:

2 + 3 + 7 + 17 = 5 + 11 + 13 = 29

A los números que satisfacen esta propiedad, es decir, a los pares consecutivos cuya descomposición en factores primos tienen la misma suma, Pomerance les llamó pares de Ruth–Aaron. Y claro está, en cosas como esta, los ordenadores son fantásticos. Pomerance descubrió que entre los números menores que 20.000 hay 26 pares de Ruth–Aaron. El mayor en este rango lo forman el 18.490 y y el 18.491.

Aunque los pares disminuían en cantidad cuando los números crecían, Pomerance conjeturó que había infinitos pares de Ruth–Aaron, pero no tenía idea de demostrar su corazonada. Su descubrimiento fue publicado en un paper de tono desenfadado en el Journal of Recreational Mathematics. Una semana después de la publicación recibió una llamada de Paul Erdös, a quien no conocía. El maestro de la teoría de números le dijo que había demostrado la conjetura y que quería ser invitado a Atlanta para mostrarla. Esto de decir “quería ser invitado” debe ser matizado y sólo se entiende si conocemos un poquito más a este excéntrico y genial matemático, así que os recuerdo unos pocos detalles que, a buen seguro, harán vuestras delicias.

Reunía todos los clichés del sabio distraído y del genio desorganizado. Comenzó su fama como niño prodigio en Hungría. A los cuatro años de edad le dijo a su madre: “si sustraes 250 de 100, obtienes -150″. A esa misma edad era capaz de multiplicar cifras de tres y cuatro dígitos sólo de cabeza.

A los 18 años causó sensación en los círculos matemáticos de Hungría al presentar una prueba sencilla del teorema de Euclides que dice que entre cualquier número entero y su doble existe, al menos, un número primo. Esta prueba ya existía y la había dado el ruso Pafnuti Lvóvitch Chebyshef, pero su prueba era demasiado extensa para figurar en los libros de texto. Erdös había proporcionado, sin embargo, una prueba sencilla y simple.

Un día llamó a la puerta de una zapatería. La empleada salió a abrir. Después de las mínimas frases de introducción la conversación fue la siguiente:

- Dígame un número de cuatro cifras.
- 2.532 - respondió la dependienta.
- Su cuadrado es 6.411.024. Lo siento, estoy perdiendo facultades y no puedo darle el cubo. ¿Cuántas pruebas del teorema de Pitágoras conoce?
- Una.
- Yo conozco 37.

Y continuó un rato haciéndole preguntas de matemáticas.

De adulto sólo pensaba en matemáticas y se dice que incluso pensaba en ellas aunque estuviera pensando en otra cosa. Escribió, sólo y en colaboración con otros, un total de 1.475 artículos académicos, muchos de ellos imprescindibles y todos muy valiosos. Hizo matemáticas en 25 países diferentes, completando teoremas en lugares remotos y a veces publicándolos en revistas poco conocidas. En su época, se decía que alguien no era un verdadero matemático si no le conocía.

Le fascinaban los problemas fáciles de plantear pero difíciles de resolver. Tenía tanta manía matemática que cuando entraba en una habitación su primera observación era: “Cuatro paredes dividido por dos ventanas”. Sus cartas solían empezar normalmente con un “Supongamos que x es …”.

Medía un metro setenta, pesaba 49 kilos y tenía el pelo blanco, así que podemos decir que su aspecto era cadavérico, demacrado o enfermizo. Si a esto añadimos que cuando iba andando por la calle iba gesticulando, siempre sumido en las matemáticas, no sé qué más se podrá decir de él.

Sólo poseía una maleta, la ropa que llevaba puesta y una radio de la época de las cavernas. Decía que la propiedad privada era una carga. A principios de los setenta llegó como profesor invitado por un año. Después de cobrar su primera paga, un mendigo le pidió el dinero que costaba una taza de té. Erdös tomó el sobre, separó una cierta cantidad para sus gastos frugales y le dio el resto al mendigo. En 1984 le dieron el Premio Wolf, el más lucrativo en matemáticas con 50.000 dólares de premio. Se quedó con 720 dólares y el resto lo donó para que se hiciera un campamento para chicos con problemas de conducta. A finales de la década de los 80 supo de un estudiante que quería estudiar matemáticas pero que no podía por problemas de dinero. Le dio 1.000 dólares prestados y que sólo se los devolviera cuando hubiera arreglado dichos problemas. Una década más tarde el estudiante quiso devolvérselos y se lo dijo a Graham, un amigo común: “¿Erdös querrá que le pague con intereses?”. Cuando Graham se lo preguntó a Erdös le contestó: “Dile que haga con los 1.000 dólares lo mismo que hice yo”.

No tenía ocupación laboral estable: daba clases aquí y allá y conferencias, y así iba tirando. Renunció absolutamente a todas las comodidades materiales, incluso tampoco tenía domicilio fijo: vivía en casas de amigos allí donde le tocaba enseñar o hacer de conferenciante. Poseía un lenguaje peculiar. Los niños eran “épsilon” (en matemáticas, épsilon es un número muy pequeño), dar clases “predicar”, el matrimonio “captura” y Dios era “FS” (fascista supremo); las mujeres eran “jefes”, los hombres “esclavos”, los casados “atrapados”, la música era “ruido” y el alcohol “veneno”. Cuando decía que alguien había muerto significaba que había dejado de hacer matemáticas. Rechazaba toda religión organizada. Un día fue a dar clase a una escuela católica y dijo que lo único que le molestaba era que hubiera tantos signos “más” (+) en las paredes. En otra ocasión le preguntaron: “¿Qué dirías a Jesucristo si te lo encontraras en la calle?” y respondió que le preguntaría si la hipótesis del continuo era verdad. Daba tres posibilidades en la contestación que debía darle este último:

a) Gödel y Cohen ya había dicho todo lo que hay que saber.
b) Sí existe respuesta, pero tu cerebro no está lo suficientemente desarrollado para entenderla.
c) El Padre, el Espíritu Santo y Yo hemos estado elucubrando sobre el particular desde mucho antes de la Creación, pero no hemos llegado a ninguna conclusión.

Y añadía que esta última respuesta sería la más amable.

Una mañana, en Nueva Jersey, se mencionó el nombre de un colega de California. En ese momento, Erdös recordó un resultado matemático que quiso compartir con él. Se levantó y fue a marcar el número. Su anfitrión le recordó que en California eran las 5 de la mañana. Erdös respondió: “Muy bien, eso significa que estará en casa”.

Escribió con otros 485 autores, por lo que se puede decir que colaboró con más gente que cualquier otro matemático en la historia. A esos 485 se dice que tienen número de Erdös 1. Si alguien ha trabajado con uno de esos 485 se dice que tiene el número de Erdös 2. Si alguien con alguno de estos últimos tendrá el número de Erdös 3 y así sucesivamente. Einstein tenía número de Erdös 2. Aun cuando estuvo bien entrado en los 70, hubo algunos años en los que publicó 50 artículos. Los buenos matemáticos escriben ese orden de publicaciones … en toda su vida.

En 1976 George Purdy y otros matemáticos estaban tomando café en el salón de la Universidad de Texas. En la pizarra que quedaba a sus espaldas había un problema de análisis funcional, un campo extraño para Erdös. Purdy sabía que dos matemáticos acababan de dar con una solución del mismo que habían condensado en 30 páginas. Erdös miró a la pizarra y dijo: “¿Qué es eso? ¿Es un problema?”. Purdy le dijo que sí. Entonces, se dirigió a la pizarra y se concentró en los enunciados, hizo unas cuantas preguntas sobre qué representaban algunos símbolos y luego, sin esfuerzo, escribió la solución en dos líneas. Los que estaban presentes se quedaron estupefactos, como si hubieran asistido a un truco de magia.

Os podría contar muchas cosas más de este fascinante personaje, pero me extendería demasiado. Hay un maravilloso libro titulado “El hombre que sólo amaba los números” que cito en fuentes y que ya os recomendé que trata de la vida y peripecias de este hombre, su pasión por las matemáticas y su lado más humano.

Pero volvamos con Pomerance y los pares de Ruth-Aaron. El encuentro derivó en una colaboración que se plasmó en 21 publicaciones. En 1995, Hank Aaron y Paul Erdös recibieron el doctorado honoris causa de la Universidad de Emory. Erdös, si bien llevaba toga y birrete, también llevaba sus sandalias. Se sentó en el podio con la cabeza entre las manos garabateando sus cuadernos de matemáticas mientras duraba la ceremonia.

Pomerance explicó todo sobre los pares de Ruth-Aaron al propio jugador Hank Aaron, quien escuchó pacientemente lo que cambió la vida del propio Pomerance. Finalmente les pidió a ambos (a Erdös y a Aaron) que le autografiaran una pelota de béisbol, lo cual hicieron con gusto; y así, Pomerance afirmó: “Hank Aaron tiene número de Erdös uno”.

Os dejo finalmente con una frase que a Erdös le gustaba decir:

Un matemático es una máquina que convierte café en teoremas.

Estos matemáticos están locos.

Fuentes:
El hombre que sólo amaba los números“, Paul Hoffman.
Un buen resumen del libro anterior

lunes, febrero 04, 2008

operación de EQUIVALENCIA en Java y Scala JVM

Capítulo 21 ScalaBook, lectura imprescindible
Especialmente para programadores Java y .net, pero muy práctico para todos
Trata sobre "¿este objeto es igual que este otro?"
Dice que en Java, se ha verificado que lo raro es encontrar este método correctamente sobreescrito (muy raro)
Es muy raro encontrar un programador de Java que sobreescriba la relación de equivalencia correctamente
Y eso puede tener consecuencias muy malas
¿Qué opina Scala de todo esto?
Primero...
El libro insiste como nadie en como debe hacerse correctamente para que no te equivoques.
Eso ayuda, pero... no es una ayuda impresionante.
Por ejemplo, Groovy (lenguaje compilado pero dinámico para la máquina virtual de Java) siempre te crea un operador de igualdad y función Hash correctas, tú puedes sobreescribirlo si quieres, pero, por defecto tienes algo que funciona
Si en Scala creas "case class" en vez de class normal, Scala te creará una función Hash y operador de comparación correctos
¿Porqué sólo si es una "case class"?
Quizá por rendimiento, pero lo importante es que si no es una "case class" funciona igual de mal que Java.
Tienes una función Hash y una función de equivalencia, que sólo sirven para dar problemas
¿No sería mejor en estos casos no tener nada y que explotara en tiempo de compilación?
Sí, pero en Java, son métodos definidos en Object. Por lo tanto todos tienen que tener implementación.
Como Java NUNCA da una implementación de estos automática para tus clases, y como es muy frecuente que tus clases no necesiten estos métodos... Java opta por...
No te voy a dar el coñazo obligándote a definir unos métodos que casi nunca te sirven para nada. Yo en Object ya te doy una implementación.
Majete que es el colega... no te da por saco continuamente, pero... eso provoca uno de los mayores puntos de error en Java (porque el pobrecito no tiene ni idea en Object de como se comparan manzanas)
Error que sólo se detecta en tiempo de ejecución y puede ser difícil de encontrar
La conclusión es que Scala en este punto es mucho mejor que Java, pero sigue sin ser una solución buena (véase Groovy)
Como siempre (y en este caso también), la recomendación de Scala es... no utilices clases con estado (asignación destructiva). Eso ayuda
¿Y QUÉ PASA CON LAS TUPLAS?
Las tuplas son inmutables (no tienen estado, no tienen asignación destructiva). Empezamos bien
Aunque en el libro no recuerdo haber leído nada al respecto, he podido comprobar que Scala sí crea método de equivalencia y Hash correctamente.
Muy buena noticia
En C++ no tienen esta paranoia
pos eso, ya me diréis como lo veis
He vuelto a leer sobre paralelismo, concurrencia, tolerancia a fallos... y me paso al lado tenebroso...
Programación declarativa, esa es la solución a los problemas de hoy y sobre todo del futuro (a no ser que llegue antes la computación cuántica de forma masiva y todos al paro)
Si me dejáis (es decir, si no recibo cientos de correos pidiendo que no os torture con eso), os iré contando
Por cierto...
¿Alguien se atreve a intentar explicarme cómo se comparan dos objetos en visual basic 6?
Es curiosidad porque no tengo ni idea