M3 - Programació estructurada / Continguts UF1: La sentència while

De wikiserver
La revisió el 18:59, 20 gen 2020 per Rsort (Discussió | contribucions) (Exemple: semàfors i comptadors alhora)
(dif) ← Versió més antiga | Versió actual (dif) | Versió més nova → (dif)
Dreceres ràpides: navegació, cerca

Sintaxi i Estructura

Per dur a terme aquest tipus de control sobre les iteracions d’un bucle, la sintaxi d’aquesta sentència en el llenguatge Java és la següent:

while (expressió booleana) {
      Instruccions per executar dins del bucle
   }

Com podeu veure, el seu format és molt semblant a la sentència if, simplement canviant la paraula clau per while. Com ja passava amb les diferents sentències dins les estructures de selecció, si entre els parèntesis es posa una expressió que no avalua un resultat de tipus booleà, hi haurà un error de compilació.

Exemple: estalviar-vos d’escriure el mateix molts cops

//Un programa que escriu una línia amb 100 caràcters ’-’.
    
public class Linia {
    
  public static void main (String[] args) {
    
    //Inicialitzem un comptador
    int i = 0;
    //Ja hem fet això 100 cops?
    while (i < 100) {
      System.out.print(’-’);
      //Ho hem fet un cop, sumem 1 al comptador
      i = i + 1;
    }
  }
}

Exemple: aprofitar un comptador

import java.util.Scanner;

//Un programa que mostra la taula de multiplicar d’un nombre.

public class TaulaMultiplicar {

  public static void main(String[] args) {

    //S’inicialitza la biblioteca.
    Scanner lector = new Scanner(System.in);
    //Pregunta el nombre.
    System.out.print("Quina taula de multiplicar vols? ");
    int taula = lector.nextInt();
    //El comptador servirà per fer càlculs.
    int i = 1;
    while (i <= 10) {
      int resultat = taula * i;
      System.out.println(taula + " * " + i + " = " + resultat);
      i = i + 1;
    }
    System.out.println("Aquesta ha estat la taula del " + taula);
  }
}

Exemple: no sempre se suma u

import java.util.Scanner;

//Anem a sumar un seguit de múltiples de tres.

public class SumarMultiplesTres {

  public static void main(String[] args) {
    
    Scanner lector = new Scanner(System.in);
    System.out.print("Fins a quin valor vols sumar múltiples de 3? ");
    int limit = lector.nextInt();
    int resultat = 0;
    int i = 0;
    while (i <= limit) {
      if ((i % 3) == 0) {
        System.out.println("Afegim " + i + "...");
        resultat = resultat + i;
      }
      i = i + 1;
    }
    System.out.println("El resultat final és " + resultat + ".");
  }
}

Ara bé, hi ha una manera de simplificar aquest programa. Seria molt més senzill si, en lloc d’anar provant un per un tots els valors dins del rang, el comptador sempre tingui únicament valors múltiples de 3. En aquest cas, només caldria anar sumant els valors sense necessitar cap estructura de selecció. Partint d’aquest fet, quins valors hauria d’anar prenent el comptador? Doncs 0, 3, 6, 9, 12, 15, etc. Si pensem una mica en aquesta seqüència de nombres, es veu que hi ha prou que el comptador s’incrementi de tres en tres.

Per tant, el codi del bucle es podria reemplaçar pel següent:

while (i <= limit) {
  System.out.println("Afegim " + i +"...");
  resultat = resultat + i;
  //Incrementem de tres en tres.
  i = i + 3;
}
  • Una variable de control amb el paper de comptador es pot modificar de qualsevol manera que es consideri escaient. Ara bé, sempre cal garantir que a cada iteració us apropeu a la condició lògica false, i eviteu així un possible bucle infinit.

Exemple: acumular càlculs

En una estructura de repetició, l’evolució del mateix resultat que s’està calculant pot ser el senyal de sortida per deixar de fer iteracions. Aquest seria el cas d’usar variables amb el paper d’acumulador. Un exemple d’aquest comportament, en què una variable es va modificant de manera que es deixa d’iterar quan aquesta conté el resultat final, és el càlcul de l’operació mòdul amb enters (%).

El mòdul calcula el residu de dividir un enter (el dividend) per un altre (el divisor). Una estratègia simple per calcular-lo és anar restant el divisor al dividend fins que ja no es pot fer més, ja que donaria negatiu. En aquest cas, el valor del dividend es va modificant directament fins a trobar la solució.

Si s’estructura pas per pas, seria:

import java.util.Scanner;

public class Modul {

  public static void main(String[] args) {

    Scanner lector = new Scanner(System.in);
    System.out.print("Quin és el dividend? ");
    int dividend = lector.nextInt();
    System.out.print("Quin és el divisor? ");
    int divisor = lector.nextInt();
    while (dividend >= divisor) {
      dividend = dividend - divisor;
    }
    System.out.println("El resultat final és " + dividend );
  }
}

Exemple: semàfors

El darrer cas que queda per estudiar és l’ús d’un semàfor per indicar de manera explícita si ja no cal fer iteracions. Aquesta estratègia d’ús de variables de control es basa en situacions en què decidir si es vol continuar fent iteracions d’un bucle no es pot predir o calcular d’acord amb un valor que va augmentant o disminuint. Simplement, cal anar repetint fins que es compleixi una condició molt concreta. Llavors, del que es disposa és d’una variable de control, normalment de tipus booleà, sobre la qual es fa una assignació explícita que provocarà que la condició lògica avaluï directament a false i se surti del bucle.

Un exemple d’aquest cas és un programa en què cal endevinar un valor secret. Mitjançant una estructura de selecció és possible establir si s’ha encertat o no, però el que no té sentit és que cada cop que es vulgui provar d’endevinar-lo, s’hagi d’executar el programa de nou. El més normal és que es pregunti a l’usuari fins que l’encerti. En aquest cas, però, no hi ha un valor que a poc a poc, gradualment, va variant fins a poder establir que cal deixar d’iterar. Es passa de cop de continuar preguntant al fet que ja no calgui, segons la condició de si s’ha encertat o no.

Aquesta es pot donar en qualsevol iteració, però és impossible estimar quan, ja que depèn totalment del valor introduït per l’usuari.

import java.util.Scanner;

//Un programa en què cal endevinar un nombre.

public class Endevina {

  public static void main(String[] args) {

    final int VALOR_SECRET = 4;
    Scanner lector = new Scanner(System.in);
    System.out.println("Comencem el joc.");
    System.out.println("Endevina el valor enter, entre 0 i 10.");
    boolean haEncertat = false;
    while (!haEncertat) {
      System.out.print("Quin valor creus que és? ");
      int valorUsuari = lector.nextInt();
      if (VALOR_SECRET != valorUsuari) {
      System.out.print("Has fallat! Torna a intentar-ho!!! ");
      } else {
          haEncertat = true;
        }
    }
    System.out.println("Enhorabona. Per fi l’has encertat!");
  } 
}

Exemple: semàfors i comptadors alhora

La categorització de les variables de control en tres models diferents no significa que una estructura de repetició sempre hagi de dependre d’una única variable. Recordeu que la condició lògica es representa com una expressió booleana i, per tant, pot ser tan complexa com es vulgui. En alguns casos, hi pot haver més d’una condició sota les quals es considera que no cal fer més iteracions d’un bucle. Torneu a fer una ullada al programa per endevinar un valor secret. Ara ja té una mica més de sentit, però encara hi ha un aspecte que potser el fa una mica estrany: el programa no s’acabarà fins que endevineu el valor secret. Continuarà preguntant una vegada i una altra fins que encerteu. Si no encerteu mai, no s’acabarà mai.

Potser seria més raonable posar un límit al nombre d’intents, de manera que si es falla més d’un cert nombre de vegades es considera que heu perdut el joc i s’acaba el programa. En aquest cas, la condició lògica ha de preveure dues possibilitats: si s’ha encertat el valor secret o si s’ha esgotat el nombre d’intents. És a dir, combinar un semàfor i un comptador.

La característica més important d’aquest problema, en què hi ha més d’una possibilitat sota la qual el bucle deixa d’iterar, és, un cop se’n surt, detectar el motiu exacte pel qual se n’ha sortit. Abans, sortir del bucle sempre implicava que s’havia encertat el valor secret, però ara ja no. En aquest cas, una manera de veure si les iteracions han acabat perquè s’ha endevinat el valor secret o perquè s’han esgotat els intents és mirant el darrer valor que s’ha entrat.

El programa que duria a terme aquest joc seria el següent.

import java.util.Scanner;

//Un programa en què cal endevinar un nombre.

public class EndevinaSemafor {

  public static void main(String[] args) {

    final int VALOR_SECRET = 4;
    final int MAX_INTENTS = 3;
    Scanner lector = new Scanner(System.in);
    System.out.println("Comencem el joc.");
    System.out.println("Endevina el valor enter, entre 0 i 10, en tres intents.");
    boolean haEncertat = false;
    int intents = MAX_INTENTS;
    int valorUsuari = 0;
    while ((!haEncertat) && (intents > 0)) {
      System.out.print("Quin valor creus que és? ");
      valorUsuari = lector.nextInt();
      intents = intents - 1;
      if (VALOR_SECRET != valorUsuari) {
        System.out.print("Has fallat! Torna a intentar-ho!!! ");
      } else {
          haEncertat = true;
        }
    }
    if (valorUsuari == VALOR_SECRET) {
      System.out.println("Enhorabona. Has encertat!");
    } else {
        System.out.println("Intents esgotats. Has perdut!");
    }
  }
}