Estructures de selecció

Entre els diferents tipus d’estructures de control que permeten establir el flux de control d’un programa, les més fàcils d’entendre són aquelles que creen bifurcacions o camins alternatius, de manera que, segons les circumstàncies, s’executi un conjunt d’instruccions o un altre. D’aquesta manera, donades diferents execucions d’un mateix codi font, part de les instruccions que s’executen poden ser diferents per a cada cas.

Les estructures de selecció permeten prendre decisions sobre quin conjunt d’instruccions cal executar en un punt del programa. O sigui, seleccionar quin codi s’executa en un moment determinat entre camins alternatius.

Tota estructura de selecció es basa en l’avaluació d’una expressió que ha de donar un resultat booleà: true (cert) o false (fals). Aquesta expressió s’anomena la condició lògica de l’estructura.

El conjunt d’instruccions que s’executarà dependrà del resultat de la condició lògica, i actuarà com una mena d’interruptor que marca el flux que cal seguir dins del programa. Normalment, aquesta condició lògica es basa en part, o en la seva totalitat, en valors emmagatzemats en variables amb un valor que pot ser diferent per a diferents execucions del programa. En cas contrari, no té sentit usar una estructura de selecció, ja que mentre s’està escrivint el programa ja es pot predir quin serà el resultat de l’expressió. Per tant, com que sempre serà el mateix, sempre s’executaran les mateixes instruccions sense que hi hagi cap bifurcació possible.

Hi ha diferents models de fluxos alternatius a l’hora d’executar instruccions, tot i que per a tots s’usa la mateixa família de sentències i una estructura similar en el codi font. Per tant, tots els aspectes destacats per a un dels models s’apliquen també a tots els altres. També cal dir que, si bé els exemples i la sintaxi descrita en aquest mòdul se centren en el llenguatge Java, la majoria de les estructures de selecció descrites són compartides amb els altres llenguatges de programació, per la qual cosa el concepte general és aplicable més enllà del Java. Només caldrà que cerqueu a la documentació la sintaxi específica per al llenguatge triat.

Una desviació temporal del camí: selecció simple

El cas més simple dins de les estructures de selecció és aquell en què hi ha un conjunt o bloc d’instruccions que només voleu que s’executin sota unes circumstàncies concretes. En cas contrari, aquest bloc és ignorat i, des del punt de vista de l’execució del programa, és com si no existís. Un exemple seria el programa d’una botiga virtual que aplica un descompte al preu final d’acord amb un cert criteri (per exemple, si la compra total és com a mínim de 100 €). En aquest cas, hi ha un conjunt d’instruccions, les que apliquen el descompte, que només s’executen quan es compleix la condició. En cas contrari, s’ignoren i el preu final és el mateix que l’original.

  •  La selecció simple, una desviació temporal en el camí d'instruccions. Imatge de Wikimedia Commons/-30
  • La selecció simple, una desviació temporal en el camí d'instruccions. Imatge de Wikimedia Commons

L’estructura de selecció simple permet controlar el fet que s’executi un conjunt d’instruccions si i només si es compleix la condició lògica (és a dir, el resultat d’avaluar la condició lògica és igual a true). En cas contrari, no s’executen.

Sintaxi i comportament

Per dur a terme aquest tipus de control sobre les instruccions del programa, cal usar una sentència if (si…). En el cas del Java, la sintaxi és la següent:

  1. instruccions del programa
  2. if (expressió booleana) {
  3. Instruccions per executar si l'expressió avalua a true (cert) - Bloc if
  4. }
  5. resta d'instruccions del programa

if

El nom de la sentència if bàsicament diu: “Si es compleix certa condició, fes això…”.

Si entre els parèntesis es posa una expressió que no avalua un resultat de tipus booleà, hi haurà un error de compilació.

La figura mostra un diagrama del flux de control d’aquesta sentència, que estableix els diferents blocs d’instruccions que s’executen en cada cas, depenent del resultat d’avaluar l’expressió booleana.

Figura Diagrama de flux de control per a una selecció simple

Un diagrama de flux de control consisteix en una subdivisió de passes seqüencials, d’acord amb les sentències i estructures de control d’un programa, que mostra els diferents camins que pot seguir el programa a l’hora d’executar les instruccions. Cada passa s’associa amb una figura geomètrica específica.

Una possible bifurcació en el camí d’un flux de control s’indica amb un rombe que conté una expressió per avaluar.

A partir d’ara, per esquematitzar el funcionament de les estructures de control s’usarà aquesta mena de diagrames. Només l’usarem per aclarir-ne el funcionament, de manera que no s’explicarà amb detall tot el conjunt de símbols que es poden usar. A l’hora d’usar-los, tampoc no se seguirà el format fins al darrer detall. És suficient que disposeu d’uns esquemes senzills que serveixin per donar-vos una idea clara del flux de control del programa per a cada estructura usada de manera visual.

Exemple: calcular un descompte

Recordeu que pot ser que Java llegeixi els enters introduïts per teclat amb els decimals indicats amb coma, i no punt, segons la configuració local.

Es vol fer un programa que apliqui un descompte a un preu depenent del seu valor. Per veure clarament que hi ha diferents camins dins de les instruccions, primer de tot establirem quines són les tasques que ha de fer el programa i en quin ordre.

El programa hauria de fer:

  1. Decidir quin és el valor mínim per optar al descompte i quant es descomptarà.
  2. Demanar que s’introdueixi el preu inicial, en euros, pel teclat.
  3. Llegir-lo.
  4. Veure si el preu introduït és igual o major que el valor mínim per optar al descompte.
    1. Si és així, s’aplica el descompte sobre el preu inicial.
  5. Mostrar el preu final.

En el pas 4 es pot observar que cal prendre una decisió d’acord amb una condició i que alguna de les tasques només es fa si aquesta es compleix (el pas I). Per tant, queda establert que cal una estructura de selecció simple. Partint d’aquí, un possible codi font que correspondria a aquest esquema seria el següent. Observeu quines instruccions es corresponen a cadascuna de les passes descrites.

Tot seguit es mostra el codi font que du a terme aquestes tasques. Compileu-lo, executeu-lo i observeu com el resultat final mostrat per pantalla és diferent segons el valor que introduïu pel teclat.

  1. import java.util.Scanner;
  2. //Un programa que calcula descomptes.
  3. public class Descompte {
  4. //PAS 1
  5. //Es fa un descompte del 8%.
  6. public static final float DESCOMPTE = 8;
  7. //Es fa descompte per compres d'un mínim de 100 euros.
  8. public static final float COMPRA_MIN = 100;
  9. public static void main (String[] args) {
  10. Scanner lector = new Scanner(System.in);
  11. //PAS 2 i 3
  12. System.out.print("Quin és el preu del producte, en euros? ");
  13. float preu = lector.nextFloat();
  14. lector.nextLine();
  15. //PAS 4
  16. //Estructura de selecció simple.
  17. //Si l'expressió avalua true, executa el bloc dins l'if.
  18. //En cas contrari, ignora'l.
  19. if (preu >= COMPRA_MIN) {
  20. //PAS I
  21. float descompteFet = preu * DESCOMPTE / 100;
  22. preu = preu - descompteFet;
  23. }
  24. //PAS 5
  25. System.out.println("El preu final per pagar és de " + preu + " euros.");
  26. }
  27. }

A la figura es representa el diagrama de flux de control d’aquest programa en concret. A la figura es visualitza a quina part del diagrama correspon cada part del codi.

Figura Diagrama de flux de control per al programa de càlcul d’un descompte

Repte 1: modifiqueu el programa perquè, en lloc de fer un descompte del 8% si la compra és de 100 € o més, apliqui una penalització de 2 € si el preu és inferior a 30 €.

Aspectes importants de la selecció simple

La introducció d’estructures de selecció en un programa complica la sintaxi del codi font, per la qual cosa cal ser molt acurat a l’hora d’escriure una sentència if. Si no ho feu, o bé el compilador donarà error, o bé el programa no es comportarà correctament, i haureu de repassar el codi font per veure on és l’error. Cal dir que el segon cas acostuma a ser molt més empipador que el primer. Per tant, val la pena fer un repàs dels aspectes més rellevants de la selecció simple. Aquestes qüestions afecten en una mesura o altra totes les estructures de selecció.

  • L’expressió booleana que denota la condició lògica pot ser tan complexa com es vulgui, però ha d’estar sempre entre parèntesis.
  • Les instruccions que cal executar si la condició és certa estan englobades entre dues claus ({, }). Aquest conjunt es considera un bloc d’instruccions associat a la sentència if (bloc if).
  • La línia on hi ha les claus o la condició no acaba mai en punt i coma (;), al contrari que altres instruccions.
  • Tot i que no és imprescindible, és un bon costum que les instruccions del bloc estiguin sagnades.

Vist això, val la pena remarcar que amb la introducció d’estructures de selecció simple també apareix per primer cop codi font amb diferents blocs d’instruccions: el del mètode principal i els associats a la sentència if. La principal característica d’aquest fet és que la relació entre blocs és jeràrquica: tots els nous blocs d’instruccions són subblocs del mètode principal. La figura mostra els diferents blocs existents en l’exemple.

Figura Blocs d’instruccions en el programa d’exemple

Com anireu veient, aquesta circumstància es repetirà en totes les estructures de control d’un programa. Per això heu de tenir molt clar on comença i acaba cada bloc, i quins blocs són subblocs d’un altre.

També val la pena fer èmfasi que, quan s’usin estructures que delimiten blocs d’instruccions diferents, identificats per estar escrits entre claus ({, }), és important sagnar cada línia. D’aquesta manera, es facilita la llegibilitat del codi font i la identificació de cada bloc d’instruccions. Si us hi fixeu, veureu que, de fet, això ja s’havia aplicat fins ara a les instruccions dins del mètode principal (també delimitades per claus), respecte a la declaració d’inici de la classe. Totes les instruccions del mètode principal estan sagnades respecte al marge de la declaració del mètode principal.

A mode de resum, la taula mostra una petita llista d’errors típics que es poden cometre en usar una sentència if.

Taula Errors típics en usar una sentència if
Missatge d’error en compilar Error comès Exemple
’(' expected Expressió no envoltada de parèntesis if preu >= COMPRA_MIN) {
’;’ expected Falta ; en alguna instrucció del bloc if preu = preu - descompteFet
Sempre executa bloc if S’ha posat ; a la sentència if if (preu >= COMPRA_MIN); {
Només fa condicionalment la primera
instrucció del bloc if,
la resta les fa sempre
Falten les claus que han d’envoltar el bloc, {…} if (preu >= COMPRA_MIN)

Vinculat al darrer error, cal esmentar que quan el bloc if només té una única instrucció, l’ús de claus és opcional. De totes maneres, és molt recomanable usar-les sempre, independentment d’aquest fet. Això facilita la identificació del bloc de codi associat a la sentència if quan s’està llegint el codi font.

  1. if (preu >= COMPRA_MIN)
  2. preu = preu - (preu * DESCOMPTE / 100);
  3.  
  4. és equivalent a
  5.  
  6. if (preu >= COMPRA_MIN) {
  7. preu = preu - (preu * DESCOMPTE / 100);
  8. }

Ara bé, alerta!

  1. if (preu >= COMPRA_MIN)
  2. float descompteFet = preu * DESCOMPTE / 100;
  3. preu = preu - descompteFet;
  4.  
  5. és equivalent a (fixeu-vos on estan ubicades les claus)
  6.  
  7. if (preu >= COMPRA_MIN) {
  8. float descompteFet = preu * DESCOMPTE / 100;
  9. }
  10. preu = preu - descompteFet;

Dos camins alternatius: la sentència ''if/else''

  • La selecció doble, una bifurcació en el camí. Imatge de Wikimedia Commons
  • La selecció doble, una bifurcació en el camí. Imatge de Wikimedia Commons

Suposeu que ara voleu fer un programa en què s’ha d’intentar endevinar un nombre entre 1 i 10. En aquest cas, i a diferència de l’anterior, ara hi ha dos escenaris excloents: o s’ha endevinat el nombre, o no s’ha endevinat. Segons de quin cas es tracti, la resposta del programa ha de ser diferent. Per tant, a més a més de codi comú per a qualsevol cas i d’un bloc que especifiqui les accions per dur a terme si es compleix la condició, ara en cal un altre que indiqui què cal fer només en cas contrari. Per poder fer això tenim l’estructura de selecció doble, la sentència if/else (si… si no…).

L’estructura de selecció doble permet controlar el fet que s’executi un conjunt d’instruccions, només si es compleix la condició lògica, i que se n’executi un altre, només si no es compleix la condició lògica/ Imatge de Wikimedia Commons

És important recordar que els dos blocs de codi són excloents. Mai no pot passar que tots dos s’acabin executant.

Sintaxi i comportament

Per dur a terme aquest tipus de control sobre les instruccions del programa, cal usar una sentència if/else (si… si no…). En el llenguatge Java, la sintaxi és la següent:

  1. if (expressió booleana) {
  2. Instruccions per executar si l'expressió l'avalua a true (cert) - Bloc if
  3. } else {
  4. Instruccions alternatives per executar si l'expressió l'avalua a false (fals) - Bloc else
  5. }

if/else

La sentència if/else bàsicament diu: “Si es compleix certa condició, fes això… i si no, això altre…”.

El bloc if té exactament les mateixes característiques que quan s’usa en una estructura de selecció simple. Aquestes es compleixen també per al cas del bloc else, amb la particularitat que no té cap expressió assignada. Simplement, quan la condició lògica de la sentència if no es compleix s’executen les instruccions del bloc else. La figura en mostra el diagrama del flux de control a escala genèrica.

Figura Diagrama de flux de control d’una selecció doble

Si per algun motiu no es posa cap instrucció dins del bloc else i es deixa un espai buit entre les dues claus, la selecció doble es comporta igual que la selecció simple. En aquest cas, és millor usar només la sentència if en lloc de if/else.

Exemple: endevina el nombre secret

Un possible codi d’un programa en què s’intenta endevinar un nombre serveix per mostrar amb més claredat com es pot usar una sentència if/else. Novament, abans de veure’l val la pena reflexionar sobre quines són les tasques que hauria de fer el programa i en quin ordre.

Bàsicament, el programa ha de fer:

  1. Decidir quin serà el nombre per endevinar.
  2. Demanar que s’introdueixi un nombre pel teclat.
  3. Llegir-lo.
  4. Veure si el nombre introduït és igual al valor secret:
    1. Si és igual que el nombre pensat, informa que s’ha encertat.
    2. Si no, informa que s’ha fallat.

Queda clar que en el pas 4 cal prendre una decisió d’acord amb una condició. El programa haurà d’emprendre dues vies d’acció diferents segons si la condició es compleix o no. Per tant, cal una estructura de selecció doble. Partint d’aquí, un possible codi font que correspondria a aquest esquema seria el següent. De nou, observeu quines instruccions es corresponen a cadascun dels passos descrits. Compileu-lo i executeu-lo per veure com funciona.

  1. import java.util.Scanner;
  2. //Un programa en què cal endevinar un nombre.
  3. public class Endevina {
  4. //PAS 1
  5. //El nombre per endevinar serà el 4.
  6. public static final int VALOR_SECRET = 4;
  7. public static void main (String[] args) {
  8. Scanner lector = new Scanner(System.in);
  9. //PAS 2 i 3
  10. System.out.println("Comencem el joc.");
  11. System.out.print("Endevina el valor enter, entre 0 i 10: ");
  12. int valorUsuari = lector.nextInt();
  13. lector.nextLine();
  14. //PAS 4
  15. //Estructura de selecció doble.
  16. //O s'endevina o es falla.
  17. if (VALOR_SECRET == valorUsuari) {
  18. //PAS I
  19. //Si l'expressió avalua true, executa el bloc dins l'if.
  20. System.out.println("Exacte! Era " + VALOR_SECRET + ".");
  21. } else {
  22. //PAS II
  23. //Si l'expressió avalua false, executa el bloc dins l'else.
  24. System.out.println("T'has equivocat!");
  25. }
  26. System.out.println("Hem acabat el joc.");
  27. }
  28. }

Tot seguit, la figura mostra un esquema de quin és el flux exacte d’execució quan es posa en marxa el programa i quin paper té cada part del codi d’acord amb la definició de la sintaxi.

Figura Esquema del flux de control d’un programa per endevinar un nombre

Repte 2: modifiqueu el programa perquè, en lloc d’un únic valor secret, n’hi hagi dos. Per guanyar, només cal encertar-ne un dels dos. La condició lògica que us caldrà ja no es pot resoldre amb una expressió composta per una única comparació. Serà més complexa.

Diversos camins: la sentència ''if/else if/else''

  •  La selecció múltiple: múltiples camins alternatius. Imatge de Wikimedia Commons
  • La selecció múltiple: múltiples camins alternatius. Imatge de Wikimedia Commons

Finalment, a l’hora d’establir el flux de control d’un programa, també hi ha la possibilitat que hi hagi un nombre arbitrari de camins alternatius, no solament dos. Per exemple, imagineu un programa que, a partir de la nota numèrica d’un examen, ha d’establir quina és la qualificació de l’alumne. Per això caldrà veure dins de quin rang es troba el nombre. En qualsevol cas, els resultats possibles són més de dos.

L’estructura de selecció múltiple permet controlar el fet que en complir-se un cas entre un conjunt finit de casos s’executi el conjunt d’instruccions corresponent.

Sintaxi i comportament

Aquest tipus de control sobre les instruccions del programa té associada la sentència if/else if/else (si és el cas, en aquest altre cas, si no és cap cas). Bàsicament, es tracta de la mateixa estructura que la sentència if/else, però amb un nombre arbitrari de blocs if (després del primer bloc, anomenats else if). En el cas del Java, la sintaxi és la següent:

  1. instruccions del programa
  2. if (expressió booleana 1) {
  3. Instruccions per executar si l'expressió 1 l'avalua a true (cert) - Bloc if
  4. } else if (expressió booleana 2) {
  5. Instruccions per executar si l'expressió 2 l'avalua a true (cert) - Bloc else if
  6. } else if (expressió booleana 3) {
  7. Instruccions per executar si l'expressió 3 l'avalua a true (cert) - Bloc else if
  8.  
  9. ...es repeteix tant cops com calgui...
  10.  
  11. } else if (expressió booleana N) {
  12. Instruccions per executar si l'expressió N avalua a true (cert) - Bloc else if
  13. } else {
  14. Instruccions alternatives si totes les expressions 1...N avaluen a false (fals) - Bloc else
  15. }
  16. resta d'instruccions del programa

ifelse ifelse

El nom de la sentència if/else if/else significa bàsicament: “Si es compleix certa condició, fes això… Si no, mira si es compleix això altre… I si no, això altre…”.

La figura mostra un esquema del flux d’execució, d’acord amb el resultat d’anar avaluant cadascuna de les condicions lògiques. En aquest cas és una mica més complicat, ja que, com es pot veure, hi ha més d’una condició lògica dins de la sentència. Cada condició es va avaluant ordenadament, des de la primera fins a la darrera, i s’executarà el codi del primer cas en què l’expressió avaluï com a cert. Un cop fet això, ja no es torna a avaluar cap altra condició restant i s’ignora la resta de blocs. Si en aquest procés es dóna el cas que cap de les expressions avalua a cert, s’executaran les instruccions del bloc else.

Figura Diagrama de flux de control d’una selecció múltiple

El punt important d’aquesta sentència és que només s’executarà un únic bloc de tots els possibles. Fins i tot en el cas que més d’una de les expressions booleanes pugui avaluar a cert, només s’executarà el bloc associat a la primera d’aquestes expressions dins de l’ordre establert en la sentència.

També és destacable el fet que el bloc else és opcional. Si no volem, no cal posar-lo. En aquest cas, si no es compleix cap de les condicions, no s’executa cap instrucció entre les incloses dins de la sentència.

Exemple: transformar avaluació numèrica a text

La manera d’usar la sentència if/else if/else es pot veure millor mitjançant el codi de l’exemple proposat anteriorment. Un cop més, abans de veure el codi es repassaran els passos que ha de fer el programa per dur a terme el seu objectiu i l’ordre que cal seguir.

  1. Demanar que s’introdueixi la nota pel teclat.
  2. Llegir-la.
  3. Mostrar un text o un altre segons el rang de valors dins del qual es troba la nota:
    1. Si és major o igual que 9 i menor o igual que 10, la nota és d’“Excel·lent”.
    2. Si és major i igual que 6,5 però estrictament menor que 9, la nota és “Notable”.
    3. Si és major i igual que 5 però estrictament menor que 6,5, la nota és “Suficient”.
    4. Si no és cap dels casos anteriors, la nota és de “Suspès”.

Aquest cop és el pas 3 el que presenta diferents possibilitats segons si es compleixen certes condicions. En aquest cas, però, hi ha més de d’un camí (n’hi ha quatre). A més a més, cal decidir si es compleix una condició diferent per decidir quin és el camí que cal seguir entre totes les opcions. El darrer es du a terme simplement quan no es compleix cap dels anteriors. Per tant, es tracta d’una selecció múltiple.

No oblideu que en una estructura de selecció la condició lògica sempre va entre parèntesis.

Un fet destacat en aquesta descripció de les tasques que ha de dur a terme el programa, com a mínim respecte als exemples anteriors, és que per a cada pas possible s’han de complir dues condicions alhora. El valor de la nota ha d’estar per sota de cert valor i per sobre d’un altre. L’expressió de tipus booleà que cal avaluar per veure quin camí cal seguir ha de comprovar totes dues coses: que es compleix una cosa i l’altra. O sigui, cal una expressió lògica basada en la conjunció de dues comparacions. Un cop més, observeu quines instruccions es corresponen a cadascun dels passos descrits. Fixeu-vos en particular en com són les condicions lògiques per a cada cas.

Compileu i executeu el programa següent per veure com la nota mostrada depèn del valor introduït:

  1. import java.util.Scanner;
  2. //Un programa que indica la nota en text a partir de la numèrica.
  3. public class Avaluacio {
  4. public static void main (String[] args) {
  5. Scanner lector = new Scanner(System.in);
  6. //PAS 1 i 2
  7. System.out.print("Quina nota has tret? ");
  8. float nota = lector.nextFloat();
  9. lector.nextLine();
  10. //PAS 3
  11. //Estructura de selecció múltiple.
  12. //S'entra al bloc on la condició lògica avaluï a true.
  13. //Si cap no ho fa, s'entra al bloc else.
  14. System.out.print("La teva nota final és ");
  15. if ((nota >= 9)&&(nota <= 10)) {
  16. //PAS I
  17. System.out.println("Excel·lent.");
  18. } else if ((nota >= 6.5)&&(nota < 9)) {
  19. //PAS II
  20. System.out.println("Notable.");
  21. } else if ((nota >= 5)&&(nota < 6.5)) {
  22. //PAS III
  23. System.out.println("Aprovat.");
  24. } else {
  25. //PAS IV
  26. System.out.println("Suspès.");
  27. }
  28. System.out.println("Espero que hagi anat bé...");
  29. }
  30. }

El diagrama de flux per a aquest programa en concret es mostra tot seguit, a la figura. En aquest cas, el codi seguirà un entre cinc camins possibles, tots disjunts.

Figura Esquema del flux de control d’un programa d’assignar la nota

Una altra possibilitat

El programa que tot just s’ha exposat analitza totes les possibilitats a l’hora d’avaluar les notes de manera molt estricta. Totes les condicions possibles són absolutament disjuntes. Mai no es pot donar el cas que dues avaluïn cert, ja que la nota introduïda pertanyerà a un i només a un dels rangs establerts. Ara bé, la selecció múltiple accepta que les condicions no ho siguin. En cas que se’n compleixi més d’una, el bloc d’instruccions que s’executarà serà el de la primera condició que ha avaluat com a true, per ordre d’escriptura en el codi.

Un cop es té una mica d’experiència programant, es pot jugar amb aquest fet, per plantejar aquest programa d’una altra manera:

  1. Demanar que s’introdueixi la nota pel teclat.
  2. Llegir-la.
  3. Mostrar un text o un altre segons dins de quin rang de valors es troba la nota:
    1. Si és major o igual que 9, la nota és “Excel·lent”.
    2. Si és major o igual que 6,5, la nota és “Notable”.
    3. Si és major o igual que 5, la nota és “Suficient”.
    4. Si no és cap dels casos anteriors, la nota és “Suspès”.

En aquest plantejament, l’ordre d’avaluació de les condicions és molt més important. Per a una nota de 9,5 es compleixen totes i cadascuna de les condicions. Però només es durà a terme l’acció relativa a la primera de les enumerades (en aquest cas, la I).

El codi font associat seria el següent, pràcticament idèntic a l’anterior. Compileu-lo i executeu-lo per comprovar que el comportament és el mateix.

  1. import java.util.Scanner;
  2. //Un programa que indica la nota en text a partir de la numèrica.
  3. public class AvaluacioSimplificat {
  4. public static void main (String[] args) {
  5. Scanner lector = new Scanner(System.in);
  6. //PAS 1 i 2
  7. System.out.print("Quina nota has tret? ");
  8. float nota = lector.nextFloat();
  9. lector.nextLine();
  10. //PAS 3
  11. //Estructura de selecció múltiple.
  12. //S'entra al bloc on la condició lògica avaluï a true.
  13. //Les condicions s'avaluen per ordre d'aparició.
  14. //Si cap no ho fa, s'entra al bloc else.
  15. System.out.print("La teva nota final és ");
  16. if ((nota >= 9)&&(nota <= 10)) {
  17. //PAS I
  18. System.out.println("Excel·lent.");
  19. } else if (nota >= 6.5) {
  20. //PAS II
  21. System.out.println("Notable.");
  22. } else if (nota >= 5) {
  23. //PAS III
  24. System.out.println("Aprovat.");
  25. } else {
  26. //PAS IV
  27. System.out.println("Suspès.");
  28. }
  29. System.out.println("Espero que hagi anat bé...");
  30. }
  31. }

Observeu atentament què passa si la nota és 8. En aquest cas, avaluen com a certes la segona (nota >= 6.5) i la tercera condició (nota >= 5), però el bloc d’instruccions que s’executarà és el de la segona. Això es veu clarament en el diagrama de la figura si aneu seguint ordenadament el flux de control establert. De fet, si us hi fixeu, el diagrama en si és com el de la figura, i només varien les condicions lògiques.

Figura Esquema del flux de control del programa alternatiu d’assignar la nota

Combinació d'estructures de selecció

Les sentències que defineixen estructures de selecció són instruccions com qualssevol altres dins d’un programa, si bé amb una sintaxi una mica més complicada. Per tant, res no impedeix que tornin a aparèixer dins de blocs d’instruccions d’altres estructures de selecció. Això permet crear una disposició de bifurcacions dins el flux de control per tal de dotar al programa d’un comportament complex, per comprovar si es compleixen diferents condicions d’acord amb cada situació.

Exemple: descompte màxim i control d'errors

Per veure un exemple en què resulta útil la combinació d’estructures de selecció, imagineu que voleu fer un parell de modificacions a l’exemple del càlcul d’un descompte. D’una banda, que hi hagi un valor màxim de descompte, de manera que si per algun motiu correspon fer un descompte per sobre d’aquest valor, només s’apliqui el màxim establert. D’altra banda, estaria bé corregir un comportament una mica estrany que ara mateix té el programa: que és capaç d’acceptar preus negatius. En aquest cas, com que és evident que l’usuari s’ha equivocat, estaria bé avisar-lo amb algun missatge.

Ara el programa hauria de fer el següent:

  1. Decidir quin és el valor mínim per optar al descompte, quant es descomptarà i el valor màxim possible.
  2. Demanar que s’introdueixi el preu inicial, en euros, pel teclat.
  3. Llegir-lo.
  4. Comprovar que el preu és correcte i no és negatiu:
    1. Si es compleix, veure si el preu introduït és igual o superior al valor mínim per optar al descompte:
      1. Si és així, calcular el descompte.
      2. Comprovar si el descompte supera el màxim permissible:
        1. Si és així, el descompte es redueix al màxim permissible.
      3. Aplicar el descompte sobre el preu inicial.
    2. Mostrar el preu final.
    3. Si el preu era negatiu, mostrar un missatge d’error.

Així, doncs, apareixen dues noves condicions. Abans de fer res cal veure si el preu és positiu i, immediatament després de calcular el descompte, cal veure si es compleix la nova condició de si el valor és superior al màxim o no. La particularitat és que ara les diferents condicions del programa només es comproven si les anteriors es van complint, una darrere de l’altra.

El codi següent fa ús d’una combinació d’estructures de selecció per dur a terme aquesta modificació. Compileu-lo i executeu-lo. Mitjançant comentaris s’associa cada instrucció a cadascun dels passos descrits.

  1. import java.util.Scanner;
  2. //Un programa que calcula descomptes.
  3. public class DescompteControlErrors {
  4. //PAS 1
  5. //Es fa un descompte del 8%.
  6. public static final float DESCOMPTE = 8;
  7. //Es fa descompte per compres de 100 euros o més.
  8. public static final float COMPRA_MIN = 100;
  9. //Valor del descompte màxim: 10 euros.
  10. public static final float DESC_MAXIM = 10;
  11. public static void main (String[] args) {
  12. Scanner lector = new Scanner(System.in);
  13. //PASSOS 2 i 3
  14. System.out.print("Quin és el preu del producte, en euros? ");
  15. float preu = lector.nextFloat();
  16. lector.nextLine();
  17. //PAS 4. El preu és positiu?
  18. if (preu > 0) {
  19. //PAS I
  20. if (preu >= COMPRA_MIN) {
  21. //PAS a
  22. float descompteFet = preu * DESCOMPTE / 100;
  23. //PAS b. Si el descompte supera el màxim, cal reduir-lo.
  24. if (descompteFet > DESC_MAXIM) {
  25. //PAS alfa. Reduir descompte
  26. descompteFet = DESC_MAXIM;
  27. }
  28. //PAS c
  29. preu = preu - descompteFet;
  30. }
  31. //PAS II. Es mostra el preu final.
  32. System.out.println("El preu final per pagar és de " + preu + " euros.");
  33. } else {
  34. //PAS III. El preu era negatiu. Cal avisar l'usuari.
  35. System.out.println("Preu incorrecte. És negatiu.");
  36. }
  37. }
  38. }

Recordeu que en Java cada bloc s’identifica per estar entre claus: {}.

La figura mostra un esquema de com queden distribuïts els blocs d’instruccions dins del programa i de com s’estructuren jeràrquicament en nivells de profunditat. Com es pot apreciar, quan es combinen estructures de control és especialment important sagnar el codi font per fer-lo intel·ligible i usar sempre claus per poder distingir clarament on comença i on acaba cada bloc diferent.

Un dels aspectes més importants d’aquest exemple és que el programa ha de ser capaç de comprovar si les dades que ha introduït l’usuari compleixen certes condicions i així avisar-lo de possibles errors. Moltes vegades, per aconseguir-ho cal combinar diferents estructures de selecció, de manera que només se segueix pel camí que processa les dades si es pot garantir que són correctes. De fet, tant en l’exemple d’endevinar un nombre com en el de transformar les notes també valdria la pena fer aquesta comprovació, i veure si el valor introduït està realment entre els valors permissibles (0 i 10).

Les estructures de selecció són fonamentals per poder comprovar si les dades que introdueix l’usuari són correctes abans de processar-les.

Figura Blocs d’instruccions en una combinació d’estructures de selecció

Repte 3: modifiqueu els exemples d’endevinar (Endevina) un nombre i transformar una nota numèrica en textual (AvaluacioSimplificat) perquè comprovin que el valor que ha introduït l’usuari es troba dins del rang de valors correcte (entre 1 i 10). En algun cas, potser no cal combinar diverses estructures de selecció…

Múltiples blocs d'instruccions i àmbit de variables

Recordeu que quan es diu “usar una variable” es vol dir en realitat “usar l’identificador associat a una variable”.

En el moment que el codi font d’un programa s’organitza en diferents blocs d’instruccions, uns dins dels altres, a causa de l’aparició d’estructures de control, cal anar amb molt de compte de respectar l’àmbit de la declaració d’una variable. La millor manera de veure què pot passar si no es té cura d’això és seguint un exemple concret.

Suposeu que ara voleu modificar el programa anterior perquè, a més del preu final, també mostri exactament quants euros s’han descomptat sobre el preu inicial. Com es disposa de la variable descompteFet ja declarada, on s’emmagatzema precisament el valor exacte del descompte, només hauria de ser qüestió de consultar-ne el valor i mostrar-lo per pantalla, tal com ja es fa amb el preu. Per tant, d’entrada, una possible modificació en el codi original seria la que es mostra tot seguit. Integreu-la en el programa original i proveu si funciona o no.

  1. ...
  2. if (preu > 0) {
  3. //PAS I
  4. if (preu >= COMPRA_MIN) {
  5. float descompteFet = preu * DESCOMPTE / 100;
  6. if (descompteFet > DESC_MAXIM) {
  7. descompteFet = DESC_MAXIM;
  8. }
  9. preu = preu - descompteFet;
  10. }
  11. System.out.println("El preu final per pagar és de " + preu + " euros.");
  12. //Modificació. Ara també mostrem el descompte... o no?
  13. System.out.println("S'ha fet un descompte de " + descompteFet + " euros.");
  14. } else {
  15. System.out.println("Preu incorrecte. És negatiu.");
  16. }
  17. ...

Malauradament, si intenteu compilar el programa modificat amb aquest nou codi, hi haurà un error en la nova línia on s’intenta mostrar el valor del descompte. Aquest us informa que l’identificador descompteFet no ha estat declarat (cannot find symbol) i, per tant, no pot ser usat. Però tot just unes línies abans, a la primera instrucció del bloc if, hi ha la declaració. Com és possible? La resposta és que, ara que ja hi ha més d’un bloc d’instruccions en el codi font del programa, no se n’ha respectat l’àmbit. Recordeu que la declaració d’una variable només té validesa des que es declara fins al delimitador de final del bloc on s’ha declarat (en aquest cas, la clau }).

Aquesta condició es compleix per a la variable preu, ja que ha estat declarada al bloc de codi del programa principal, i per tant pot ser usada fins a la clau que tanca el mètode principal (és a dir, a tot el programa). Ara bé, la variable descompteFet ha estat declarada en el bloc if i, per tant, deixa de tenir validesa en acabar el bloc. Com que es consulta més enllà d’aquest bloc, és com si no s’hagués declarat mai.

Per solucionar aquest problema, la declaració s’ha de dur amb anterioritat a intentar mostrar-la per pantalla i, alhora, en el mateix bloc d’instruccions, no dins del bloc if. Això es podria fer com es mostra a continuació. Incorporeu el canvi al codi original i proveu si ara funciona.

  1. ...
  2. if (preu > 0) {
  3. //Si es declara aquí, té validesa fins al final d'aquest bloc if.
  4. float descompteFet = 0;
  5. if (preu >= COMPRA_MIN) {
  6. descompteFet = preu * DESCOMPTE / 100;
  7. if (descompteFet > DESC_MAXIM) {
  8. descompteFet = DESC_MAXIM;
  9. }
  10. preu = preu - descompteFet;
  11. }
  12. System.out.println("El preu final per pagar és de " + preu + " euros.");
  13. System.out.println("S'ha fet un descompte de " + descompteFet + " euros.");
  14.  
  15. } else {
  16. System.out.println("Preu incorrecte. És negatiu.");
  17. }
  18. ...

La figura mostra quin era l’àmbit inicial de la variable descompteFet en el programa original (A) i quin és el que resulta de la modificació de la línia on es declara (B) per tal de solucionar l’error.

Figura Àmbits de la variable descompteFet

Una bona pregunta que us podeu fer és quin sentit té llavors declarar variables dins de blocs de codi que no siguin el mètode principal. La resposta és que, a escala de funcionament del programa, no hi ha cap diferència. Es podrien declarar totes a l’inici del programa principal, per garantir que mai no us trobeu fora del seu àmbit, i tot funcionaria igual. Ara bé, declarar variables tot just en el moment que s’usen té sentit amb vista a la llegibilitat del programa. Si declareu una variable prop del codi on se’n fa ús, és més senzill per a algú altre que llegeixi el codi font establir-ne el valor sense haver de repassar tot el codi des del principi. Això és especialment útil en programes llargs.

El commutador: la sentència ''switch''

Hi ha una estructura de selecció una mica especial, motiu pel qual s’ha deixat per al final. El que la fa especial és que no es basa a avaluar una condició lògica composta per una expressió booleana, sinó que estableix el flux de control a partir de l’avaluació d’una expressió de tipus enter o caràcter (però mai real).

  •  Una sentència ''switch'' és un selector entre diferents valors./5
  • Una sentència ''switch'' és un selector entre diferents valors.

La sentència switch enumera, un per un, un conjunt de valors discrets que es volen tractar, i assigna les instruccions que cal executar si l’expressió avalua en cada valor diferent. Finalment, especifica què cal fer si l’expressió no ha avaluat en cap dels valors enumerats. És com un commutador o una palanca de canvis, en què s’assigna un codi per a cada valor possible que cal tractar. Aquest comportament seria equivalent a una estructura de selecció múltiple en què, implícitament, totes les condicions són comparar si una expressió és igual a cert valor. Cada branca controlaria un valor diferent. El cas final és equivalent a l‘else.

De fet, en la majoria de casos, aquesta sentència no aporta res des del punt de vista del flux de control que no es pugui fer amb una selecció múltiple. Però és molt útil amb vista a millorar la llegibilitat del codi o facilitar la generació del codi del programa. Ara bé, sí que hi ha un petit detall en què aquesta sentència aporta alguna cosa que la resta d’estructures de selecció no poden fer directament: executar de manera consecutiva més d’un bloc de codi relatiu a diferents condicions.

Sintaxi i comportament

La sintaxi de la sentència switch s’ajusta al format descrit tot seguit. Novament, es basa en una paraula clau seguida d’una condició entre parèntesis i un bloc de codi entre claus. Ara bé, la diferència respecte de les sentències vistes fins ara és que els conjunts d’instruccions assignats a cada cas independent no es distingeixen entre si per claus. Aquests es van enumerant com un conjunt d’apartats etiquetats per la paraula clau case, seguida del valor que es vol tractar i de dos punts. De manera opcional, es pot posar una instrucció especial que serveix de delimitador de final d’apartat, anomenada break. Al final de tots els apartats se’n posa un d’especial, anomenat default, que no ha d’acabar mai en break.

  1. switch(expressió de tipus enter) {
  2. case valor1:
  3. instruccions si l'expressió avalua a valor1
  4. (opcionalment) break;
  5. case valor2:
  6. instruccions si l'expressió avalua a valor2
  7. (opcionalment) break;
  8. ...
  9. case valorN:
  10. instruccions si l'expressió avalua a valorN
  11. (opcionalment) break;
  12. default:
  13. instruccions si l'expressió avalua a algun valor que no és valor1...valorN
  14. }

switch

El nom de la sentència switch vol dir bàsicament: “commuta entre aquestes accions d’acord amb un valor seleccionat”.

En executar la sentència switch s’avalua l’expressió entre parèntesis i immediatament s’executa el conjunt d’instruccions assignat a aquell valor entre els diferents apartats case. Si no n’hi ha cap amb aquest valor entre els disponibles, llavors s’executa l’apartat etiquetat com a default (per defecte).

Cal dir que els valors numèrics no han de seguir cap ordre en concret per definir cada apartat. Si bé pot ser més polit enumerar-los per ordre creixent, això no té cap efecte sobre el comportament del programa.

La particularitat especial d’aquesta sentència la tenim en l’ús de la instrucció break. Aquesta indica què cal fer un cop executades les instruccions de l’apartat. Si apareix la instrucció, el programa se salta la resta d’apartats i continua amb la instrucció posterior a la sentència switch (després de la clau que tanca el bloc). En aquest aspecte, si tots els apartats acaben en break, el funcionament de switch és equivalent a fer:

  1. if (expressió == valor 2) {
  2. instruccions si l'expressió avalua a valor1
  3. } else if (expressió == valor2) {
  4. instruccions si l'expressió avalua a valor2
  5. ...
  6. } else if (expressió == valorN) {
  7. instruccions si l'expressió avalua a valorN
  8. } else {
  9. instruccions si l'expressió avalua a algun valor que no és valor1...valorN
  10. }

break

La paraula clau break és com dir “trenca, surt fora d’aquesta sentència switch”.

Ara bé, si algun dels apartats no acaba en break, en executar la darrera instrucció, en lloc de saltar la resta d’apartats, el que es fa és seguir executant les instruccions de l’apartat que ve immediatament després. Així anirà fent, apartat per apartat, fins trobar-ne algun que acabi en break. La figura mostra aquest comportament tan particular.

Figura Diagrama de flux de control d’una sentència switch

Exemple simple: diferents opcions en un menú

La millor manera d’entendre com es comporta la sentència switch és veient un parell d’exemples. Per començar, es mostrarà el cas més senzill, en què tots els apartats tenen una instrucció break al final i, per tant, és com tenir una selecció múltiple en què cada cas correspon directament a una condició clarament diferenciada amb el seu propi bloc d’instruccions independent dels altres.

Un ús molt típic de la sentència switch és per generar de manera senzilla menús en què l’usuari pot triar entre diferents opcions. Segons l’opció triada, s’executen directament les instruccions associades a cadascuna. Suposeu un programa en què, a partir dos nombres enters, es demana quina operació es vol fer entre aquests: sumar, restar, multiplicar o dividir.

El programa hauria de fer el següent:

  1. Preguntar els dos nombres enters.
  2. Llegir-los.
  3. Mostrar un menú amb les quatre opcions possibles, enumerades d’1 a 4.
  4. Llegir l’opció triada.
  5. Segons l’opció:
    1. Si és 1 se sumen els dos enters.
    2. Si és 2 es resten.
    3. Si és 3 es multipliquen.
    4. Si és 4 es divideixen.
    5. Si és cap altra cal informar que l’opció és incorrecta.
  6. Finalment es mostra el resultat per pantalla.

El codi per fer això mitjançant la sentència switch podria ser el següent. Compileu-lo, executeu-lo i observeu-ne el comportament.

  1. import java.util.Scanner;
  2. //Un programa que dóna diferents opcions de càlcul, usant la sentència switch.
  3. public class Opcions {
  4. public static void main (String[] args) {
  5. Scanner lector = new Scanner(System.in);
  6. //PAS 1 i 2
  7. System.out.print("Introdueix un valor enter: ");
  8. int primerValor = lector.nextInt();
  9. lector.nextLine();
  10. System.out.print("Introdueix-ne un altre: ");
  11. int segonValor = lector.nextInt();
  12. lector.nextLine();
  13. //PAS 3 i 4
  14. System.out.println("Quina operació vols fer? ");
  15. System.out.println("[1] Sumar");
  16. System.out.println("[2] Restar");
  17. System.out.println("[3] Multiplicar");
  18. System.out.println("[4] Dividir");
  19. System.out.print("Selecciona l'opció [1-4]: ");
  20. int opcio = lector.nextInt();
  21. lector.nextLine();
  22. int resultat = 0;
  23. //PAS 5
  24. switch(opcio) {
  25. //PAS I
  26. case 1:
  27. System.out.print("Has triat sumar...");
  28. resultat = primerValor + segonValor;
  29. break;
  30. //PAS II
  31. case 2:
  32. System.out.print("Has triat restar...");
  33. resultat = primerValor - segonValor;
  34. break;
  35. //PAS III
  36. case 3:
  37. System.out.print("Has triat multiplicar...");
  38. resultat = primerValor * segonValor;
  39. break;
  40. //PAS IV
  41. case 4:
  42. System.out.print("Has triat dividir...");
  43. resultat = primerValor / segonValor;
  44. break;
  45. //PAS V
  46. default:
  47. System.out.println("Opció no prevista.");
  48. }
  49. //PAS 6
  50. System.out.println("El resultat és " + resultat + ".");
  51. }
  52. }

La figura mostra un esquema de les instruccions executades segons alguns valors d’exemple. En lloc d’usar un diagrama de flux de control, s’usa directament el codi com a referència per fer la relació més aclaridora. Com es pot veure, l’opció default és especialment útil per controlar valors d’entrada no vàlids.

Figura Exemple de flux de control del programa d’opcions de càlcul

Exemple amb propagació d'instruccions: els dies d'un mes

És el moment de veure què succeeix si falten instruccions break en alguns apartats. El primer que podeu provar és treure els break de l’exemple anterior. Veureu què passa. Cada cop que trieu una opció s’executarà aquella opció i totes les posteriors (ho podreu veure, ja que s’anirà mostrant per pantalla el missatge per a cada operació). Per tant, l’únic resultat vàlid serà el darrer i sempre estareu dividint.

Un altre exemple diferent bastant típic és el de calcular quants dies té un mes. A grans trets, el programa faria el següent:

  1. Preguntar el número de mes (un valor de tipus enter).
  2. Llegir-lo.
  3. Segons el valor del mes:
    1. Si és 2 cal dir que el nombre de dies és 28 o 29.
    2. Si és 4, 6, 9 o 11 cal dir que el nombre de dies és 30.
    3. Si és 1, 3, 5, 7, 8, 10 o 12 cal dir que el nombre de dies és 31.
    4. Si és cap altre, cal dir que que s’ha introduït un número de mes incorrecte.

Ara bé, ara es jugarà amb la propagació d’instruccions quan manca el break per establir dins del codi conjunts de valors que donen el mateix resultat. En triar un valor, s’executen les instruccions del cas associat a aquell valor i, mentre no trobi un break, continua executant la resta de casos consecutivament, l’un darrere de l’altre. Si un cas està buit, no fa res i salta directament al següent. Com es pot veure a l’exemple, no cal que els valors per a cada cas estiguin ordenats d’acord amb cap ordre numèric concret. Un cop més, el cas default serveix per controlar valors fora de rang. Comproveu que el programa fa el que se n’espera en el vostre entorn de desenvolupament.

  1. import java.util.Scanner;
  2.  
  3. //A veure quants dies té un mes...
  4. public class DiesDelMes {
  5.  
  6. public static void main (String[] args) {
  7. Scanner lector = new Scanner(System.in);
  8.  
  9. //PAS 1 i 2
  10. System.out.print("Quin número de mes vols analitzar [1-12]? ");
  11. int mes = lector.nextInt();
  12. lector.nextLine();
  13.  
  14. //PAS 3
  15. System.out.print("Els dies d'aquest mes deuen ser...");
  16. switch(mes) {
  17. //PAS I
  18. case 2:
  19. System.out.println("28 o 29!");
  20. break;
  21. //PAS II
  22. case 4:
  23. case 6:
  24. case 9:
  25. case 11:
  26. System.out.println("30!");
  27. break;
  28. //PAS III
  29. case 1:
  30. case 3:
  31. case 5:
  32. case 7:
  33. case 8:
  34. case 10:
  35. case 12:
  36. System.out.println("31!");
  37. break;
  38. default:
  39. System.out.println("Aquest mes no existeix!");
  40. }
  41.  
  42. }
  43.  
  44. }

La figura mostra un parell d’exemples de quin és el flux de control de la sentència switch per a certs valors. Tingueu en compte que el programa comença pel valor que coincideix amb el resultat d’avaluar l’expressió i que, un cop allà, va avançant de manera seqüencial per tots els casos, hi hagi el que hi hagi, fins a trobar una sentència break.

Figura Exemple de flux de control per al programa dels dies del més. Una sentència switch amb casos sense break

Finalment, cal dir que, com ja s’ha esmentat, no hi ha res que faci la sentència switch que no pugui fer una selecció múltiple. L’elecció de switch se sol fer exclusivament per motius de claredat del codi. Compareu el codi anterior amb el següent, equivalent. Tot seguit, comproveu que fa el mateix.

  1. import java.util.Scanner;
  2.  
  3. //A veure quants dies té un mes...
  4. public class DiesDelMesIf {
  5.  
  6. public static void main (String[] args) {
  7. Scanner lector = new Scanner(System.in);
  8.  
  9. System.out.print("Quin número de mes vols analitzar [1-12]? ");
  10. int mes = lector.nextInt();
  11. lector.nextLine();
  12.  
  13. System.out.print("Els dies d'aquest mes deuen ser...");
  14. if (mes == 2) {
  15. System.out.println("28 o 29!");
  16. } else if ((mes == 4)||(mes == 6)||(mes == 9)||(mes == 11)) {
  17. System.out.println("30!");
  18. } else if ((mes == 1)||(mes == 3)||(mes == 5)||(mes == 7)||(mes == 8)||(mes == 10)||(mes == 12)) {
  19. System.out.println("31!");
  20. } else {
  21. System.out.println("Aquest mes no existeix!");
  22. }
  23. }
  24.  
  25. }

Decidir quin és més pràctic és decisió vostra. De totes maneres, no sol ser bona idea tenir expressions tan llargues que se surtin del camp de visió en l’editor emprat.

Control d'errors en l'entrada bàsica mitjançant estructures de selecció

Les estructures de selecció són especialment útils com a mecanisme per controlar si les dades que ha introduït un usuari són correctes abans d’usar-les dins del programa. Un cop ja sabeu com funcionen, és un bon moment per ampliar les explicacions sobre l’entrada bàsica de dades per teclat, de manera que sigui possible controlar si el valor que s’ha introduït encaixa amb el tipus de dada esperat per a cada instrucció de lectura.

Si es parteix que l’extensió per llegir dades de teclat s’ha inicialitzat anteriorment com sempre:

  1. Scanner lector = new Scanner(System.in);

Abans de llegir qualsevol dada introduïda pel teclat, és possible usar les instruccions de la taula per establir si la dada que tot just s’ha escrit pel teclat és d’un tipus concret o no. Quan s’usen, l’usuari pot introduir dades pel teclat (el programa s’atura) i s’avalua si el que ha escrit pertany a un tipus de dada o un altre. Aquestes instruccions es consideren expressions booleanes que avaluen com a true si el tipus concorda i com a false si no és el cas.

Un cop usada aquesta instrucció, només es podran llegir les dades correctament usant la instrucció lector.next… corresponent al mateix tipus si ha avaluat com a true. Si s’intenta després que hagi avaluat false, l’error d’execució està garantit.

Taula Instruccions per a la lectura de dades pel teclat
Instrucció Tipus de dada controlada
lector.hasNextByte() byte
lector.hasNextShort() short
lector.hasNextInt() int
lector.hasNextLong() long
lector.hasNextFloat() float
lector.hasNextDouble() double
lector.hasNextBoolean() boolean

La instrucció associada a una cadena de text no existeix, ja que qualsevol dada escrita pel teclat sempre es pot interpretar com a text. Per tant, la lectura mai no pot donar error.

Per exemple, el programa pensat per endevinar un nombre es podria fer tal com mostra el codi següent. Pareu atenció al comportament de les combinacions d’estructures de selecció. Fixeu-vos que hi ha tres camins disjunts possibles fruit d’aquest fet: si la dada escrita no és un enter, si ho és però no s’encerta el nombre, i finalment, si ho és i sí que s’encerta. Compileu-lo i executeu-lo per veure que, efectivament, ara es pot detectar que les dades introduïdes no són del tipus correcte.

  1. import java.util.Scanner;
  2. //Un programa en què cal endevinar un nombre.
  3. public class EndevinaControlErrorsEntrada {
  4. //PAS 1
  5. //El nombre per endevinar serà el 4.
  6. public static final int VALOR_SECRET = 4;
  7. public static void main (String[] args) {
  8. Scanner lector = new Scanner(System.in);
  9. System.out.println("Comencem el joc.");
  10. System.out.print("Endevina el valor enter, entre 0 i 10: ");
  11. boolean tipusCorrecte = lector.hasNextInt();
  12. if (tipusCorrecte) {
  13. //S'ha escrit un enter correctament. Ja es pot llegir.
  14. int valorUsuari = lector.nextInt();
  15. lector.nextLine();
  16. if (VALOR_SECRET == valorUsuari) {
  17. System.out.println("Exacte! Era " + VALOR_SECRET + ".");
  18. } else {
  19. System.out.println("T'has equivocat!");
  20. }
  21. System.out.println("Hem acabat el joc.");
  22. } else {
  23. //No s'ha escrit un enter.
  24. System.out.println("El valor introduït no és un enter.");
  25. }
  26. }
  27. }

Repte 4: apliqueu el mateix tipus de control sobre les dades de l’entrada a l’exemple del càlcul del descompte.

Solucions dels reptes proposats

Repte 1:

  1. import java.util.Scanner;
  2. public class Penalitzacio {
  3. public static final float PENALITZACIó = 2;
  4. //Es fa descompte per compres de 100 euros o més.
  5. public static final float COMPRA_MIN = 30;
  6. public static void main (String[] args) {
  7. Scanner lector = new Scanner(System.in);
  8. System.out.print("Quin és el preu del producte en euros? ");
  9. float preu = lector.nextFloat();
  10. lector.nextLine();
  11. if (preu < COMPRA_MIN) {
  12. //PAS I
  13. preu = preu + PENALITZACIó;
  14. }
  15. System.out.println("El preu final per pagar és de " + preu + " euros.");
  16. }
  17. }

Repte 2:

  1. import java.util.Scanner;
  2. public class DosSecrets {
  3. //PAS 1
  4. //El nombres per endevinar seran el 4 i el 7.
  5. public static final int VALOR_SECRET_UN = 4;
  6. public static final int ALTRE_VALOR_DOS = 7;
  7. public static void main (String[] args) {
  8. Scanner lector = new Scanner(System.in);
  9. //PAS 2 i 3
  10. System.out.println("Comencem el joc.");
  11. System.out.print("Endevina el valor enter, entre 0 i 10: ");
  12. int valorUsuari = lector.nextInt();
  13. lector.nextLine();
  14. //PAS 4
  15. //Estructura de selecció doble.
  16. //O s'endevina o es falla.
  17. if ((VALOR_SECRET_UN == valorUsuari)||(ALTRE_VALOR_DOS == valorUsuari)) {
  18. //PAS I
  19. //Si l'expressió avalua true, executa el bloc dins l'if.
  20. System.out.println("Exacte! Era " + valorUsuari + ".");
  21. } else {
  22. //PAS II
  23. //Si l'expressió avalua false, executa el bloc dins l'else.
  24. System.out.println("T'has equivocat!");
  25. }
  26. System.out.println("Hem acabat el joc.");
  27. }
  28. }

Repte 3:

  1. import java.util.Scanner;
  2. public class EndevinaControlValors {
  3. public static final int VALOR_SECRET = 4;
  4. public static void main(String[] args) {
  5. Scanner lector = new Scanner(System.in);
  6. System.out.println("Comencem el joc.");
  7. System.out.print("Endevina el valor enter, entre 0 i 10: ");
  8. int valorUsuari = lector.nextInt();
  9. lector.nextLine();
  10. if ((valorUsuari < 0) || (valorUsuari > 10)){
  11. System.out.println("Error: el valor ha d'estar entre 0 i 10.");
  12. }else{
  13. if (VALOR_SECRET == valorUsuari) {
  14. System.out.println("Exacte! Era " + VALOR_SECRET + ".");
  15. }else{
  16. System.out.println("T'has equivocat!");
  17. }
  18. System.out.println("Hem acabat el joc.");
  19. }
  20. }
  21. }
  1. import java.util.Scanner;
  2. public class AvaluacioControlValors {
  3. public static void main(String[] args) {
  4. Scanner lector = new Scanner(System.in);
  5. System.out.print("Quina nota has tret? ");
  6. float nota = lector.nextFloat();
  7. lector.nextLine();
  8. if ((nota >= 9) && (nota <= 10)) {
  9. System.out.println("La teva nota final és Excel·lent.");
  10. } else if ((nota >= 6.5) && (nota < 9)) {
  11. System.out.println("La teva nota final és Notable.");
  12. } else if ((nota >= 5) && (nota < 6.5)) {
  13. System.out.println("La teva nota final és Aprovat.");
  14. } else if ((nota >= 0) && (nota < 5)) {
  15. System.out.println("La teva nota final és Suspès.");
  16. } else {
  17. System.out.println("El valor escrit no està entre 0 i 10!");
  18. }
  19. }
  20. }

Repte 4:

  1. import java.util.Scanner;
  2. public class DescompteControlErrorsEntrada {
  3. //Es fa un descompte del 8%.
  4. public static final float DESCOMPTE = 8;
  5. //Es fa descompte per compres de 100 euros o més.
  6. public static final float COMPRA_MIN = 100;
  7. //Valor del descompte màxim: 10 euros.
  8. public static final float DESC_MAXIM = 10;
  9. public static void main (String[] args) {
  10. Scanner lector = new Scanner(System.in);
  11. System.out.print("Quin és el preu del producte en euros? ");
  12. boolean tipusCorrecte = lector.hasNextFloat();
  13. //El tipus és correcte?
  14. if (tipusCorrecte) {
  15. float preu = lector.nextFloat();
  16. lector.nextLine();
  17. if (preu > 0) {
  18. if (preu >= COMPRA_MIN) {
  19. float descompteFet = preu * DESCOMPTE / 100;
  20. if (descompteFet > DESC_MAXIM) {
  21. descompteFet = DESC_MAXIM;
  22. }
  23. preu = preu - descompteFet;
  24. }
  25. System.out.println("El preu final per pagar és de " + preu + " euros.");
  26. } else {
  27. System.out.println("Preu incorrecte. És negatiu.");
  28. }
  29. } else {
  30. //No s'ha escrit un enter.
  31. System.out.println("El valor introduït no és un enter.");
  32. }
  33. }
  34. }
Anar a la pàgina anterior:
Annexos
Anar a la pàgina següent:
Activitats