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?



  1. Yo voy a decir que sale 1 por pantalla mientras abro el eclipse para probarlo…

  2. :-)

  3. Pues, yo creo que ese código podría estar mucho mejor escrito. Porque así no se entiende nada. Es muy poco mantenible en mi opinión.

    Pero pensándolo mejor, seguro que sale un Null Pointer Exception.

    a no, que en java no hay punteros..

  4. Yo creo que sale por pantalla: -3
    y si estoy equivocado, una explicacion no estaria demas…

  5. Mmmm, interesante… Prometo que lo he acertado, aunque lo he comprobado antes de escribir el comentario :)

  6. Hola, soy nuevo aquí y por lo que se ve, este blog lleva sin mucha actividad desde hace unos añitos, pero me he tentado a escribir en él para resolver tu duda @Jaime, y que quede resuelta por si otro visitante se deja caer por aquí.

    Para empezar, el resultado mostrado en pantalla es “1″, tal y como ha dicho @jorge y seguramente @pablo también lo consiguiera. Ahora bien ¿por qué?

    Bueno, lo primero de todo, es estar con @eneko, en que este tipo de código es de lo más enrevesado que hay (lo hay peor -siguiendo esta travesura de los operadores- como os diré al final); pero la intención creo que habrá sido simplemente hacer que la gente se rompa un poco el coco y aplique conocimientos tan interesantes e importantes como los operadores de (pre/post)incremento y (pre/post)decremento, así como aspectos esenciales como la notación de infijo y refrescar la precedencia de estos operadores; haber metido unas multiplicaciones*** habría sido todavía más entretenido.

    Empecemos por el principio. Así, de primeras, ese churro no deja de ser eso, un churro, y estaría bien separar un poco los diferentes operadores que intervienen y, como siempre en caso de duda, poner paréntesis para saber qué es lo que va a ir primero. Se podría hacer un cambio de notación (a la Polaca, que no necesita paréntesis; o utilizar un árbol de sintaxis) pero se puede sintetizar fácilmente con los paréntesis.

    Para separar operadores, hay que utilizar o conocer la tabla de Precedencia de Operadores en Java[1]. En esta tabla, se indican los operadores que van a empezar a evaluarse antes que otros. El problema adicional que tiene este puzzle, es que los operadores de (pre/post)incremento y (pre/post)decremento están formados a su vez por los operadores de suma y resta habituales. Por último, seguramente se tendrá que echar mano de lo que se denomina comúnmente Asociatividad del Operador[2]: y es que [(5 - 3) - 2 => 2 - 2 => 0] != [5 - (3 - 2) => 4 - 1 => 3].

    Así que empezamos a destripar: el método es sencillo, cada vez que encontremos un operador candidato de mayor prioridad, lo separamos por un espacio:
    i = –i – i++ + i– – i++ + i – i–; ¿Está claro, no?

    Para ir avanzando, y resumiendo aquí lo que interesa conocer, tenemos que buscar, de entre todos los operadores (de los que aparecen en esa expresión), el menos prioritario: el de asignación; o sea, el igual. De este, cabe recordar que tiene la Asociatividad “de Derecha a Izquierda”. Eso quiere decir, que a la variable inmediatamente a la derecha, se le asignará toda la subexpresión que quede a su izquierda.

    Así que: “i = (–i – i++ + i– – i++ + i – i–);”. Era obvio, ¿verdad?

    Ahora, de los que hay ahí, los siguientes en prioridad serían las sumas y restas (no confundir con los operadores unarios de número positivo o número negativo, de los que, por suerte, no hay): la Asociatividad de estos es la ‘natural’, es decir “de Izquierda a Derecha”. Por lo tanto, empezamos por la izquierda de la subexpresión en busca de esos dos operadores. No quiero abusar de los paréntesis, pero bueno…
    i = ( (–i – i++ + i– – i++ + i) – (i–) );
    i = ( ( (–i – i++ + i– – i++) + i) – (i–) );
    i = ( ( ( (–i – i++ + i–) – (i++) ) + i) – (i–) );
    i = ( ( ( ( (–i – i++) + (i–) ) – (i++) ) + i) – (i–) );
    i = ( ( ( ( ( (–i) – (i++) ) + (i–) ) – (i++) ) + i) – (i–) );
    ¡Menudo castillo!

    Bueno, aunque pueda ser difícil a primera vista, no cabe la menor duda de lo qué es más prioritario. Por lo tanto, empezamos a evaluar y deshacer el castillo de dentro a fuera; lo que algunos llaman Evaluación Impaciente, justo lo contrario de la Evaluación Perezosa, tan común en los Lenguajes Funcionales más o menos puros.

    i = ( ( ( ( ( (–i) – (i++) ) + (i–) ) – (i++) ) + i) – (i–) );
    [act. i = 0]
    i = ( ( ( ( ( (-1) – (i++) ) + (i–) ) – (i++) ) + i) – (i–) );
    [act. i = -1 ]
    i = ( ( ( ( ( (-1) – (-1) ) + (i–) ) – (i++) ) + i) – (i–) );
    [act. i = 0 ]
    i = ( ( ( ( ( (-1) – (-1) ) + (0) ) – (i++) ) + i) – (i–) );
    [act. i = -1 ]
    i = ( ( ( ( ( (-1) – (-1) ) + (0) ) – (-1) ) + i) – (i–) );
    [act. i = 0 ]
    i = ( ( ( ( ( (-1) – (-1) ) + (0) ) – (-1) ) + 0) – (i–) );
    [act. i = 0 ]
    i = ( ( ( ( ( (-1) – (-1) ) + (0) ) – (-1) ) + 0) – (0) );
    [act. i = -1 ]

    Ahora, se deshacen las operaciones elementales de primero de primaria, cambiando los menos-menos por sumas, podando paréntesis quitando los valores nulos y operando:
    i = ( ( ( ( ( (-1) – (-1) ) + (0) ) – (-1) ) + 0) – (0) );
    i = ( ( (-1) – (-1) ) – (-1) );
    i = ( ( (-1) + 1 ) + 1 );
    i = ( ( 0 ) + 1 );
    i = ( 1 );

    Cabe destacar una cosa importante: los dos pasos anteriores se hacen de una tirada gracias a que se suele utilizar la Notación Polaca y ahí va todo a saco; pero he preferido separarlo para que quedara claro utilizando paréntesis; no porque sea así.

    Y, por último, como no podía ser de otra forma, se asigna el valor numérico “1″ a la variable ‘i’.

    Observaciones
    Hablando del tema de los operadores en postfijo, cabe destacar que no se pueden concatenar, ya que el resultado de i++ no es una variable, sino un valor. Esto tiene mucha miga que cortar, pero para sintetizar, no se pueden hacer construcciones como i++++ o (i++)++ ni tampoco juntarlos ++i++ de estas extrañas maneras. De todas formas, os animo a ver algo más abajo las construcciones de hasta cinco operadores de estos seguidos y razonar porqué esos sí valen.

    Referencias
    [1] http://es.wikibooks.org/wiki/Programaci%C3%B3n_en_Java/Precedencia_de_operadores
    [2] La que encontré de Java estaba en un ppt, así que enlazo la C++ que, en lo que importa, coinciden: http://es.wikipedia.org/wiki/Anexo:Operadores_de_C_y_C%2B%2B

    *** Bola Extra
    Como no sé si el autor de este blog leerá el comentario, por si es necesario, incluyo una versión que da una vuelta más de rosca a estos aspectos. ¿Qué pasaría si remplazamos la línea de operadores por esta otra?
    i = –i-i—i–+–i-+–i+i–*–i-i++-++i+i+i/i+++i;
    ¡Tiene sorpresa! :P

  7. Estaba a punto de ir a comer, cuando he querido dudar del método que usé y probar a organizar los operadores de otra forma cuando me encontré con la siguiente característica (no sé si intencional o no), así que antes de continuar, me gustaría conocer la opinión del maestro. Lo que encontré fue esto:
    i = (–i) – (i++) + (i–) – (i++) + (i) – (i–); (la que salió con mi método)
    i = (–i) – (i++) + (i) – (–i) + (++i) – (i–);
    i = (–i) – (i) + (++i) – (–i) + (++i) – (i–);
    Pues bien, de esas tres formas (las demás no se adaptan a la sintaxis correcta que indico en las observaciones, al final del otro comentario), las tres dan el mismo resultado: un “1″. Yo creo que es pura casualidad, y por lo tanto, intencionado… consciente o inconscientemente. Pero bueno, no deja de ser sorprendente; aunque, debido a la desnormalización que implican las otras formas de separarlos, quiero suponer que en escenarios más complejos, los resultados serían diferentes y sólo una válida.

    Además, por si sirve de algo, añado que en caso de que dichos operadores incrementales no existieran (salvo al final, ya que esos dos “–” estarían restando a nada), la expresión sin ninguna ambigüedad quedaría así:
    i = ( ((((-(-i)) – (i)) + (+(+i))) – (-(-i)) + (+(+i))) – (i–) );
    Que, para nuestra desgracia, da como resultado un feo “0″.

    Ah, también quería recordar, cosa que debería hacerse al principio, que la pseudoimplementación de los operadores de preincremento y predecremento sería algo tal que así:
    int ++( int i ){
    int valor = i + 1;
    return valor; // ¡no retorna la variable i!
    }
    int –( int i ){
    int valor = i – 1;
    return valor; // ¡no retorna la variable i!
    }

    Y las de postincremento y postdecremento, así:
    int ( int & i )++{ // quiero remarcar que ‘i’ se pasa ‘por referencia’
    int valor = i;
    i = i + 1;
    return valor; // en este tampoco
    }
    int ( int & i )–{ // y también aquí se pasa ‘por referencia’
    int valor = i;
    i = i – 1;
    return valor; // ni en este
    }

  8. Dos cosas de las que me acabo de dar cuenta. La primera, es que la concatenación de dos signos menos seguidos le ha sentado mal al parser de WP. Si Raúl lo puedes cambiar, ¡te lo agradecería mucho!
    Por si acaso, dejo una forma alternativa para la bola extra.
    i = __i-i__-i__+__i-+__i+i__*__i-i++-++i+i+i/i+++i;
    o
    i = ——i-i——-i——+——i-+——i+i——*——i-i++-++i+i+i/i+++i; // no sé si se verán bien
    Entiéndase que el __ es el símbolo de dos menos seguidos.

    La segunda cosa es más seria. Por las prisas de querer añadirlo, y que en un minuto marcho a andar en bici (soy una persona que intenta cumplir la rutina), se me ha colado una mala implementación de los operadores prefix, ya que en todos la variable i es pasada por referencia y modificada:

    int ++( int & i ){
    _ int valor = (i = i + 1);
    _ return valor; // ¡no retorna la variable i!
    }
    int ——( int & i ){
    _ int valor = i = i – 1; // como vimos, no son necesarios los paréntesis
    _ return valor; // ¡no retorna la variable i!
    }

    Un saludo y gracias por adelantado, @raul.

Dejar un comentario



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