jueves, 17 de marzo de 2011

CONTROL DE EJECUCIÓN

CONTROL DE EJECUCIÓN

TEMA 4
- true y false
- if - else
- Iteración
   - do - while
   - for
   - El operador coma
- Sintaxis foreach
- return
- break y continue
- La despreciada instrucción "goto"
- switch


Al igual que las criaturas sensibles, un programa debe manipular su mundo y tomar decisiones durante la ejecución. En Java, las decisiones se toman mediante las instrucciones de control de ejecución.

En este tema vamos a ver las instrucciones de control de ejecución.

TRUE Y FALSE
Todas las instrucciones condicionales utilizan la veracidad o la falsedad de una expresión condicional para seleccionar una ruta de ejecución. Es decir, se compararán una serie de valores mediante los operadores relacionales y dependiendo del resultado (true o false) la ejecución irá por un camino u otro.


IF - ELSE

Esta instrucción es la forma más básica de controlar el flujo de un programa. La opción else es opcional, tendríamos:
   if (expresión - booleana)
      instrucción

o
   if (expresión - booleana)
      instrucción1
   else
      instrucción2

La expresión - booleana producirá un valor true o false. En el primer caso si es true, pasará a ejecutar la instrucción o instrucciones (si son varias, encerradas entre llaves {}) en caso contrario no hará nada. En el segundo caso si se cumple la expresión - booleana ejecutará la instrucción o instrucciones instrucción1, si no se cumple se ejecutará la instrucción o instrucciones instrucción2. Vamos a ver un ejemplo:
//: control/IfElse.java
import static net.mindview.util.Print.*;

public class IfElse {
  static int result = 0;
  static void test(int testval, int target) {
    if(testval > target)
      result = +1;
    else if(testval < target)
      result = -1;
    else
      result = 0; // Match
  }
  public static void main(String[] args) {
    test(10, 5);
    print(result);
    test(5, 10);
    print(result);
    test(5, 5);
    print(result);
  }
} /* Output:
1
-1
0
*///:~

En el método test() podemos ver una instrucción "else if" ya que dentro de un if puede haber varias comparaciones, el "else" final se ejecutaría en caso de que no se cumplieran ninguna de las condiciones anteriores.

ITERACIÓN

Para los bucles de ejecución tenemos while, do - while y for, también llamadas instrucciones de iteración. Un bucle while sería:
   while (expresión - booleana)
      instrucción

La expresión - booleana se comprueba cuando la ejecución llega al bucle, en caso de que se cumpla, se ejecuta la o las instrucciones, cada vez que se ejecutan estas instrucciones se vuelve a comprobar si se sigue cumpliendo la expresión - booleana. Se ejecutan instrucciones mientras se cumpla la expresión - booleana. Vamos a ver un ejemplo, generamos números aleatorios hasta que se cumpla una determinada condición.
//: control/WhileTest.java
// Demonstrates the while loop.

public class WhileTest {
  static boolean condition() {
    boolean result = Math.random() < 0.99;
    System.out.print(result + ", ");
    return result;
  }
  public static void main(String[] args) {
    while(condition())
      System.out.println("Inside 'while'");
    System.out.println("Exited 'while'");
  }
} /* (Execute to see output) *///:~

En el método condition() el método random() genera un valor double comprendido entre 0 y 1 (incluye 0, pero no 1). La variable result es el resultado de una operación de comparación < que genera un boolean. En main tenemos un bucle while que se va a repetir mientras la variable result del método condition() sea true. Si no lo véis claro con este ejercicio lo he modificado un poco para que veáis cuando se deja de cumplir que Math.random()<0.99:
//: control/WhileTest2.java
// Demonstrates the while loop.

public class WhileTest2 {
  static boolean condition() {
      boolean result=true;
      double centinela=Math.random();
      System.out.println("Valor aleatorio: "+centinela);
      if (centinela<0.99)
         result=true;
      else
         result=false;
      System.out.print(result+", ");
      return result;
  }
  public static void main(String[] args) {
    while(condition())
      System.out.println("Inside 'while'");
    System.out.println("Exited 'while'");
  }
} /* (Execute to see output) *///:~


do - while
El bucle do - while tiene la forma:
do
   instrucción
while (expresión - booleana)

Este bucle se ejecuta al menos una vez a diferencia del bucle while. La instrucción o instrucciones que están entre do y while se ejecutan una vez y posteriormente se evalúa la expresión - booleana, si es true se vuelven a ejecutar la instrucción o instrucciones y si es false salimos del bucle.

for
Es quizá el bucle más utilizado. Tiene la forma:
for (inicialización; expresión - booleana; paso)
   Instrucción

Tenemos una inicialización de una variable que utilizaremos como centinela o contador. En la expresión - booleana comprobamos mediante una expresión condicional si esa variable ha alcanzado un valor determinado y el paso es el aumento o disminución que hacemos de la variable inicial. Cualquiera de las expresiones inicialización, expresión - booleana o paso puede estar vacía. La expresión - booleana se comprueba antes de cada iteración y la instrucción o instrucciones se ejecutarán mientras ésta sea true, cuando sea false saldremos del bucle. Cada vez que se ejecutan las instrucciones la variable de paso aumenta o disminuye. El bucle for se repite mientras se cumpla la expresión - booleana.
Vamos a ver un ejemplo:
//: control/ListCharacters.java
// Demonstrates "for" loop by listing
// all the lowercase ASCII letters.

public class ListCharacters {
  public static void main(String[] args) {
    for(char c = 0; c < 128; c++)
      if(Character.isLowerCase(c))
        System.out.println("value: " + (int)c +
          " character: " + c);
  }
} /* Output:
value: 97 character: a
value: 98 character: b
value: 99 character: c
value: 100 character: d
value: 101 character: e
value: 102 character: f
value: 103 character: g
value: 104 character: h
value: 105 character: i
value: 106 character: j
...
*///:~

Vemos que hay un bucle for en el que se inicializa la variable c, cuyo ámbito se restringe al bucle for. La expresión - booleana que se comprueba es c<128 y el paso es c++, la variable c aumenta en uno en cada iteración. Este bucle se repetirá 128 veces.
Se utiliza la clase envoltorio java.lang.Character, que permite tratar una variable char como un objeto. El método Character.isLowerCase(c) comprueba si el carácter c es una letra minúscula.
Mientras que en C las variables se definen al principio de un bloque para reservar la memoria necesaria, en Java se puede definir una variable en el lugar en que se necesite.

Ejercicio 1
. Escribe un programa que imprima los valores comprendidos entre 1 y 100.
Ejercicio 2. Escribe un programa que genere 25 valores int aleatorios. Para cada valor, utilice una instrucción if - else para clasificarlo como mayor que, menor que o igual a un segundo valor generado aleatoriamente.
Ejercicio 3. Modifica el Ejercicio 2 para que el código quede rodeado por un bucle while "infinito". De este modo, el programa se ejecutará hasta que lo interrumpa desde el teclado (normalmente pulsando Control - C).
Ejercicio 4. Escribe un programa que utilice dos bucles for anidados y el operador de módulo (%) para detectar e imprimir números primos (números enteros que no son divisibles por ningún número excepto por sí mismos y por 1).
Ejercicio 5. Repite el Ejercicio 10 del capítulo anterior, utilizando el operador ternario y una comprobación de tipo bit a bit para mostrar los unos y ceros en lugar de Integer.toBinaryString().

El operador coma
El operador coma sólo se utiliza en la expresión de control de un bucle for. Tanto en la parte de la inicialización como en la parte correspondiente al paso de la expresión de control, se pueden incluir instrucciones separadas por comas que serán evaluadas secuencialmente. Podemos definir dentro de la instrucción for múltiples variables, todas deben ser del mismo tipo:
//: control/CommaOperator.java

public class CommaOperator {
  public static void main(String[] args) {
    for(int i = 1, j = i + 10; i < 5; i++, j = i * 2) {
      System.out.println("i = " + i + " j = " + j);
    }
  }
} /* Output:
i = 1 j = 11
i = 2 j = 4
i = 3 j = 6
i = 4 j = 8
*///:~

El int de la instrucción for es tanto para i como para j. La parte de la inicialización puede tener cualquier número de definiciones de un mismo tipo. Definir variables en una expresión de control está limitada a los bucles for. Tanto en la inicialización como en la parte de paso las instrucciones se evalúan secuencialmente.

SINTAXIS FOREACH

Se trata de una sintaxis for nueva para utilizarla con matrices y contenedores (lo veremos más adelante). Con la sintaxis foreach (para todos) no es necesario crear una variable int para efectuar un recuento a través de una secuencia de elementos, el bucle for genera cada elemento automáticamente.
En el siguiente ejemplo tenemos una matriz de float y queremos seleccionar cada elemento de la matriz:
//: control/ForEachFloat.java
import java.util.*;

public class ForEachFloat {
  public static void main(String[] args) {
    Random rand = new Random(47);
    float f[] = new float[10];
    for(int i = 0; i < 10; i++)
      f[i] = rand.nextFloat();
    for(float x : f)
      System.out.println(x);
  }
} /* Output:
0.72711575
0.39982635
0.5309454
0.0534122
0.16020656
0.57799757
0.18847865
0.4170137
0.51660204
0.73734957
*///:~

La matriz se rellena con un antiguo bucle for. La sintaxis foreach es de la forma:
   for (float x:f)

Mediante esta expresión definimos una variable x de tipo float y asignamos secuencialmente cualquier elemento de f a x.
Cualquier método que devuelve una matriz es un buen candidato para emplearlo con la sintaxis foreach. Vamos a verlo con un ejemplo:
//: control/ForEachString.java

public class ForEachString {
  public static void main(String[] args) {
    for(char c : "An African Swallow".toCharArray() )
      System.out.print(c + " ");
  }
} /* Output:
A n   A f r i c a n   S w a l l o w
*///:~

El método toCharArray() devuelve una matriz de char, como vemos la salida de este método puede ser utilizada con la sintaxis foreach.
Esta sintaxis funciona con cualquier objeto de tipo Iterable (lo veremos más adelante).
Muchas instrucciones for requieren ir paso a paso a través de una secuencia de valores enteros:
   for (int i=0;i<100;i++)

Para este tipo de instrucciones la sintaxis foreach no funcionará a menos que creemos primero una matriz de valores int. Para simplificar esta tarea el autor ha creado un método range() en net.mindview.util.Range que genera y devuelve la matriz apropiada. La intención es que el método range() se utilice como importación de tipo static:
//: control/ForEachInt.java
import static net.mindview.util.Range.*;
import static net.mindview.util.Print.*;

public class ForEachInt {
  public static void main(String[] args) {
    for(int i : range(10)) // 0..9
      printnb(i + " ");
    print();
    for(int i : range(5, 10)) // 5..9
      printnb(i + " ");
    print();
    for(int i : range(5, 20, 3)) // 5..20 step 3
      printnb(i + " ");
    print();
  }
} /* Output:
0 1 2 3 4 5 6 7 8 9
5 6 7 8 9
5 8 11 14 17
*///:~

El método range() está sobrecargado por lo que se pueden utilizar diferentes listas de argumentos. La primera forma sobrecargada, range(10), empieza en 0 y genera valores hasta el extremo superior del rango sin incluir éste. La segunda forma, range(5,10), comienza con el primer valor y termina en el último valor menos 1. La tercera forma, range(5,20,3), incluye un valor de paso, realizándose los incrementos según este valor.
El método range() permite la utilización de foreach en más lugares, sin embargo, es menos eficiente, por lo que si necesitamos utilizar el programa a la máxima velocidad, conviene que utilicemos un perfilador, que es una herramienta que mide el rendimiento del código.
El método printnb() es equivalente a System.out.print, es decir, no genera un carácter de nueva línea, todo lo que escribe lo coloca en la misma línea.
La sintaxis foreach ahorra tiempo a la hora de escribir código, facilita la lectura y comunica perfectamente qué es lo que estamos tratando de hacer (obtener cada elemento de la matriz), en lugar de cómo lo estamos haciendo (estoy creando un índice para poder utilizarlo con cada elemento de la matriz).

RETURN
La palabra clave return es un salto incondicional, es decir, es un salto en el flujo de ejecución que se produce sin realizar previamente comprobación alguna. Esta palabra clave tiene dos objetivos: especifica qué valor devolverá un método (si no es void) y hace que la ejecución salga del método actual devolviendo ese valor. Vamos a verlo con un ejemplo:
//: control/IfElse2.java
import static net.mindview.util.Print.*;

public class IfElse2 {
  static int test(int testval, int target) {
    if(testval > target)
      return +1;
    else if(testval < target)
      return -1;
    else
      return 0; // Match
  }
  public static void main(String[] args) {
    print(test(10, 5));
    print(test(5, 10));
    print(test(5, 5));
  }
} /* Output:
1
-1
0
*///:~

Cuando se ejecuta la instrucción return salimos del método.
Si un método devuelve void y no se incluye return, habrá una instrucción return implícita al final de ese método, así pues no es necesario incluirla. Pero si el método va a devolver cualquier valor distinto de void, hay que asegurarse de que todas las rutas de ejecución devuelvan un valor.

Ejercicio 6
. Modifica los dos métodos test() de los dos programas anteriores (IfElse e IfElse2) para que admitan dos argumentos adicionales, begin y end, y para que se compruebe testval para ver si se encuentra dentro del rango comprendido entre begin y end (ambos incluidos).

BREAK Y CONTINUE

Se puede controlar el flujo de un bucle mediante break y continue. Break provoca la salida del bucle sin ejecutar el resto de instrucciones, mientras que continue detiene la ejecución de la iteración actual y vuelve al principio del bucle para comenzar con la siguiente iteración. Vamos a ver un ejemplo:
//: control/BreakAndContinue.java
// Demonstrates break and continue keywords.
import static net.mindview.util.Range.*;

public class BreakAndContinue {
  public static void main(String[] args) {
    for(int i = 0; i < 100; i++) {
      if(i == 74) break; // Out of for loop
      if(i % 9 != 0) continue; // Next iteration
      System.out.print(i + " ");
    }
    System.out.println();
    // Using foreach:
    for(int i : range(100)) {
      if(i == 74) break; // Out of for loop
      if(i % 9 != 0) continue; // Next iteration
      System.out.print(i + " ");
    }
    System.out.println();
    int i = 0;
    // An "infinite loop":
    while(true) {
      i++;
      int j = i * 27;
      if(j == 1269) break; // Out of loop
      if(i % 10 != 0) continue; // Top of loop
      System.out.print(i + " ");
    }
  }
} /* Output:
0 9 18 27 36 45 54 63 72
0 9 18 27 36 45 54 63 72
10 20 30 40
*///:~

Recuerda que el método range(100) devuelve una matriz de valores entre 0 y 99. Tanto en el primer bucle for, como en el segundo que utiliza foreach cuando el valor de i llega a 74 finaliza el bucle (break), no alcanza el 100. Y cuando el resto de la división entre i y 9 es diferente de 0 vuelve al principio del bucle (continue) y vemos que System.out.print(i+" "); ni siquiera se ejecuta, esta instrucción se ejecutará cuando i sea divisible por 9 (i%9==0). Luego tenemos un bucle while infinito del que salimos cuando j=1269 (break) y cuando el resto de dividir i por 10 es diferente de 0 volvemos al principio del bucle (continue). Las instrucciones que están por debajo de break y continue no se ejecutan.
Un bucle infinito también se puede poner de la forma for(;;).

Ejercicio 7. Modifica el Ejercicio 1 para que el programa termine usando la palabra clave break con el valor 99. Intente utilizar return en su lugar.

LA DESPRECIADA INSTRUCCIÓN "GOTO"
La instrucción goto ha estado presente en muchos lenguajes de programación desde el principio de la Informática. Goto representó el origen de las técnicas de control de programa en los lenguajes ensambladores: "Si se cumple A salta aquí, en caso contrario salta allí".
Una instrucción goto es un salto en el nivel de código fuente, de ahí su mala reputación. ¿No hay una manera de reorganizar el código para que flujo de control no tenga que dar saltos? Edsger Dijkstra publicó el artículo "Goto considered harmful" en el que se cuestionó esta instrucción, desde entonces esta instrucción ha sido despreciada por muchos programadores.
Sin embargo, el problema no es el uso del goto sino su abuso. En determinadas ocasiones incluso su uso es la mejor opción.
Goto es una palabra reservada en Java pero no se utiliza. Hay dos instrucciones que se utilizan para salir de una iteración y que se asemejan a un salto, son break y continue. Estas instrucciones se asocian a goto porque utilizan la misma técnica: una etiqueta.
Una etiqueta es un identificador seguido de dos puntos:
   etiqueta1:

La etiqueta se coloca justo antes de una instrucción de iteración. La única razón para utilizar una etiqueta es si vamos a anidar otra iteración o una instrucción switch (que veremos después). Si las etiquetas break y continue interrumpen el bucle actual, cuando se las usa como etiqueta interrumpen todos los bucles hasta el lugar donde la etiqueta se haya definido:
   etiqueta1:
   iteración - externa{
      iteración - interna{
         //...
         break; // (1)
         //...
         continue; // (2)
         //...
         continue etiqueta1; // (3)
         //...
         break etiqueta1; // (4)
      }
   }

En (1) salimos de la iteración interna y acabamos en la externa. En (2) volvemos al principio de la iteración interna. En (3) nos salimos tanto de la iteración interna como de la externa y nos situamos en etiqueta1, a continuación continúa con la iteración pero comenzando por la iteración externa. En (4) nos salimos de las dos iteraciones, nos situamos en etiqueta1 pero no volvemos a entrar en la iteración, las dos iteraciones han finalizado. Veamos un ejemplo:
//: control/LabeledFor.java
// For loops with "labeled break" and "labeled continue."
import static net.mindview.util.Print.*;

public class LabeledFor {
  public static void main(String[] args) {
    int i = 0;
    outer: // Can't have statements here
    for(; true ;) { // infinite loop
      inner: // Can't have statements here
      for(; i < 10; i++) {
        print("i = " + i);
        if(i == 2) {
          print("continue");
          continue;
        }
        if(i == 3) {
          print("break");
          i++; // Otherwise i never
               // gets incremented.
          break;
        }
        if(i == 7) {
          print("continue outer");
          i++; // Otherwise i never
               // gets incremented.
          continue outer;
        }
        if(i == 8) {
          print("break outer");
          break outer;
        }
        for(int k = 0; k < 5; k++) {
          if(k == 3) {
            print("continue inner");
            continue inner;
          }
        }
      }
    }
    // Can't break or continue to labels here
  }
} /* Output:
i = 0
continue inner
i = 1
continue inner
i = 2
continue
i = 3
break
i = 4
continue inner
i = 5
continue inner
i = 6
continue inner
i = 7
continue outer
i = 8
break outer
*///:~

Si nos fijamos en el segundo bucle for, cuando i==2, volvemos al principio de este bucle (instrucción continue). Si i==3 nos salimos del bucle for (instrucción break), antes de salir tenemos que incrementar i ya que al salirnos con break de este bucle, la variable i no se incrementa y vamos a volver a entrar en este bucle for, porque el for que está por encima es infinito. Cuando i==7 volvemos al principio de los dos bucles (continue outer), es necesario también incrementar i, ya que el bucle for interior no realiza el incremento ya que con esta instrucción vamos al bucle for exterior. Cuando alcanzamos i==8 salimos de los dos bucles for (instrucción break outer), sin esta instrucción no habría manera de salir del bucle externo desde el bucle interno ya que el bucle externo es un bucle infinito.
En aquellos casos en los que salir de un bucle implique salir de un método basta con ejecutar return.
Vamos a ver un ejemplo de break y continue con un bucle while:
//: control/LabeledWhile.java

// While loops with "labeled break" and "labeled continue."

import static net.mindview.util.Print.*;

public class LabeledWhile {
  public static void main(String[] args) {
    int i = 0;
    outer:
    while(true) {
      print("Outer while loop");
      while(true) {
        i++;
        print("i = " + i);
        if(i == 1) {
          print("continue");
          continue;
        }
        if(i == 3) {
          print("continue outer");
          continue outer;
        }
        if(i == 5) {
          print("break");
          break;
        }
        if(i == 7) {
          print("break outer");
          break outer;
        }
      }
    }
  }
} /* Output:
Outer while loop
i = 1
continue
i = 2
i = 3
continue outer
Outer while loop
i = 4
i = 5
break
Outer while loop
i = 6
i = 7
break outer
*///:~

Cuando i==1 vuelve al principio del bucle while interior (instrucción continue). Si i==3 volvemos a la etiqueta outer (instrucción continue outer), a continuación entramos en los dos bucles while. Cuando i==5 nos salimos del bucle while interior (instrucción break). Si i==7 nos salimos del bucle while interior y exterior (instrucción break outer), es decir nos salimos de todos los bucles hasta el que tiene la etiqueta, incluyendo este último.
En Java las etiquetas solo se utilizan si tenemos bucles anidados y queremos ejecutar las instrucciones break o continue a través de más de un nivel.

SWITCH

Esta instrucción permite seleccionar entre diferentes fragmentos de código basándose en el valor de una expresión entera. Su forma es:
   switch (selector - entero){
      case valor - entero1 : instrucción; break;
      case valor - entero2 : instrucción; break;
      case valor - entero3 : instrucción; break;
      case valor - entero4 : instrucción; break;
      //...
      default : instrucción;
   }

Selector - entero es una expresión que genera un valor entero. Switch compara selector - entero con cada valor - entero. Si hay coincidencia se ejecuta la correspondiente instrucción (una instrucción o varias). Si no hay ninguna coincidencia se ejecuta default.
Cada case finaliza con una instrucción break, esto significa que cuando haya una coincidencia del selector - entero con el valor - entero, se ejecutará la instrucción correspondiente y la ejecución saldrá de switch. Sin embargo, la instrucción break es opcional. Si no se incluye se ejecutarán las instrucciones case situadas a continuación hasta que haya una instrucción break. Este comportamiento no es el deseado pero puede ser útil para programadores avanzados. La instrucción default no finaliza con break, aunque si lo consideramos oportuno lo podemos incluir.
La instrucción switch permite realizar selecciones donde haya que elegir entre diversas rutas de ejecución, pero requiere de un selector que se evalúe para dar un valor entero como int o char. Si queremos emplear una cadena de caracteres o un número en coma flotante como selector no funcionará con switch. Vamos a ver un ejemplo en que se crean letras de manera aleatoria y se determina si son vocales o consonantes:
/: control/VowelsAndConsonants.java

// Demonstrates the switch statement.

import java.util.*;
import static net.mindview.util.Print.*;

public class VowelsAndConsonants {
  public static void main(String[] args) {
    Random rand = new Random(47);
    for(int i = 0; i < 100; i++) {
      int c = rand.nextInt(26) + 'a';
      printnb((char)c + ", " + c + ": ");
      switch(c) {
        case 'a':
        case 'e':
        case 'i':
        case 'o':
        case 'u': print("vowel");
                  break;
        case 'y':
        case 'w': print("Sometimes a vowel");
                  break;
        default:  print("consonant");
      }
    }
  }
} /* Output:
y, 121: Sometimes a vowel
n, 110: consonant
z, 122: consonant
b, 98: consonant
r, 114: consonant
n, 110: consonant
y, 121: Sometimes a vowel
g, 103: consonant
c, 99: consonant
f, 102: consonant
o, 111: vowel
w, 119: Sometimes a vowel
z, 122: consonant
...
*///:~

Random.nextInt(26) genera un valor entre 0 y 25, sumamos 'a' para generar las letras minúsculas, de esta forma 'a' se convierte a int para realizar la suma. Para imprimir c es necesario hacer una proyección sobre char, sino se imprimiría un entero. Los caracteres entre comillas de las instrucciones case también generan valores enteros que se emplean para la comparación.
Las sentencias case se pueden apilar para proporcionar múltiples coincidencias para un determinado fragmento de código. Si no ponemos break al final de la instrucción case, al encontrar una coincidencia no nos saldríamos de switch sino que seguiríamos procesando el caso siguiente.

Ejercicio 8
. Crea una instrucción switch que imprima un mensaje para cada case, y coloque el switch dentro de un bucle for en el que se pruebe cada uno de los valores de case. Incluya una instrucción break después de cada case y compruebe los resultados; a continuación, elimine las instrucciones break y vea lo que sucede.
Ejercicio 9. Una secuencia de Fibonacci es la secuencia de números 1, 1, 2, 3, 5, 8, 13, 21, 34, etc., donde cada número (a partir del tercero) es la suma de los dos anteriores. Cree un método que tome un entero como argumento y muestre esa cantidad de números de Fibonacci comenzando por el principio de la secuencia; por ejemplo, si ejecuta java Fibonacci 5 (donde Fibonacci es el nombre de la clase) la salida sería: 1, 1, 2, 3, 5.
Ejercicio 10. Un número vampiro tiene un número par de dígitos y se forma multiplicando una pareja de números que contengan la mitad del número de dígitos del resultado. Los dígitos se toman del número original en cualquier orden. No se permiten utilizar parejas de ceros finales. Entre los ejemplos tendríamos:
1260=21*60
1827=21*87
2187=27*81
Escriba un programa que determine todos los números vampiro de 4 dígitos (problema sugerido por Dan Forhan).

2 comentarios:

  1. Hola Mayte, excelente blog;) la verdad es que me esta siendo muy útil. Querría hacertte una pregunta: Hasta que tema crees que debería hacer de este libro para comenzar a programar en Android?
    Te comento que tengo conocimientos intermedios de python y Java no es la primera vez que lo toco.
    Gracias!
    PD: Mi propósito es llegar a hacer un videojuego o aplicación en Android, algún dia xD.

    ResponderEliminar
  2. Hola Oparyn:
    Si puedes verte todos los temas mejor, pero si dices que ya tienes algún conocimiento de Java, te puedes poner ya mismo a programar en Android.
    Te recomiendo para empezar con blogs como:
    http://androideity.com/
    http://www.sgoliver.net/blog/?page_id=3011 (Este blog para verlo todo de manera general y el anterior ya para cosas más específicas)
    También el libro Hello Android! está muy bien.
    Está claro que cuanto mejor sepas Java, más fácil te va a resultar aprender Android, pero si hay algo que no entiendas siempre te quedará Google :-)
    Cuando ya sepas más o menos qué puedes programar en Android, ponte a hacer una aplicación sencilla que eso te va a servir de mucho.
    Un saludo y gracias.

    ResponderEliminar