M3 - Programació estructurada / Continguts UF1: Taules

De wikiserver
La revisió el 12:04, 17 feb 2018 per Rsort (Discussió | contribucions) (Emmagatzematge de l’entrada de dades en una taula)
Dreceres ràpides: navegació, cerca

Taules (Arrays)

  • Conjunt de variables que conté valors tots del mateix tipus.
  • El nombre de posició de l'element s'anomena l'índex.
Taules
  • Conté N element.
  • Per referir-se als diferents elements de la taula s'utilitza el nom de la taula seguit per l'índex entre claudàtors ([]).
  • Primer element de la taula té índex 0.
  • Els elements són: c[0], c[1], c[2]...

Declaració i creació de taules

  • La següent declaració i expressió crea una taula, que conté 12 elements int, i emmagatzema la referència

de la taula en la variable c:

int c[] = new int[12];
  • Aquesta tasca també pot realitzar-se en dos passos, com es mostra a continuació:
int c[ ]; // declara la variable taula

c = new int[12]; // crea la taula; ho assigna a la variable tipus taula
  • En crear una taula, cadascun dels seus elements rep un valor predeterminat: zero per als elements numèrics, false per als elements boolean.
  • Quan es declara un taula, el seu tipus i els claudàtors poden combinar-se al principi de la declaració per indicar que tots els identificadors a la declaració són variables tipus taula. Per exemple, la declaració
double[] taula1, taula2;
indica que taula1 i taula2 són variables tipus “taula de double”. L'anterior declaració és
equivalent a:
double taula1[];
double taula2[];
o
double[] taula1;
double[] taula2;

Inicialització de taules

  • Es pot crear un taula i inicialitzar els seus elements amb una llista d'expressions separades per comes (llista inicialitzadora) tancada entre claus ({ i });
  • la longitud de la taula es determina sobre la base del nombre d'elements en la llista inicialitzadora. Per exemple,

la declaració:

int arrayEnters[] = { 10, 20, 30, 40, 50 };
crea un taula de cinc elements amb els valors d'índexs 0, 1, 2, 3 i 4. L'element arrayEnters [0] s'inicialitza amb 10, arrayEnters [1] s'inicialitza amb 20, i així en endavant.
Aquesta declaració no requereix que new creï l'objecte taula.
La grandària de la taula queda determinada pel nombre d'elements de la llista inicialitzadora.
Taules

Manipulació de dades

  • No és possible usar l’identificador de l’array directament per invocar operacions i així manipular les dades contingudes.
Per exemple, no és possible fer el següent:
int[] a = {10, 20, 30, 40, 50};
int[] b = {50, 60, 70, 80, 100};
int[] c = a + b;
  • Les dades emmagatzemades dins d’arrays nomès es poden manipular de manera individual, posició per posició.
  • Cada posició d’un array té exactament el mateix comportament que una variable de tipus primitiu.

Exemples

Inicialització de la taula mitjançant codi

L’aplicació següent crea una taula de 10 elements i assigna a cada element un dels enters parells del 2 al 20.

  • La propietat length es dóna la longitud d’una taula
public class InicialitzacioTaula {
 
     public static void main(String args[]) {

       final int LONGITUD = 10; // declara la constant
       int taula[] = new int[LONGITUD]; // crea la taula
       // calcula el valor per a cada element de la taula
       int comptador = 0;
       while (comptador < taula.length) {
         taula[comptador] = 2 + 2 * comptador;
         comptador++;
       }
     }
   }

Sumar els elements d’una taula

public class SumaTaula {
  
     public static void main(String args[]) {

       int taula[] = {87, 68, 94, 100, 83, 78, 85, 91, 76, 87};
       int total = 0;
       // suma el valor de cada elemento al total
       int comptador = 0;
       while (comptador < taula.length) {
         total = total + taula[ comptador];
         comptador++;
       }
       System.out.println("Total dels elements de la taula: "+ total);
     }
   }

Emmagatzematge de l’entrada de dades en una taula

Com s’ha esmentat, els arrays són especialment útils per poder emmagatzemar de manera eficient un nombre arbitrari de dades provinents del sistema d’una entrada (per exemple, des del teclat). Normalment, les dades s’aniran llegint una per una, i a mesura que es faci, caldrà anar-les assignant a cadascuna de les posicions de l’array a partir del seu índex. Tot seguit veureu diferents esquemes per dur a terme aquesta tasca. Si bé els exemples se centraran en l’ús del teclat, ja que és el sistema d’entrada que per ara sabeu utilitzar, heu de tenir en compte que aquests esquemes seran aplicables a qualsevol altre mecanisme d’entrada (per exemple, un fitxer). L’única condició és que les dades estiguin en forma de seqüència, de manera que es puguin anar llegint una per una, ordenadament.

Entrada de seqüències de valors per teclat

Els arrays tenen sentit quan el programa ha de processar moltes dades, especialment provinents de l’entrada del programa (com el teclat). Abans de continuar, val la pena veure com es poden llegir seqüències de dades entrades des del teclat en una sola línia de text, de manera que sigui senzill emmagatzemar-les dins d’un array. Si bé ja sabeu com llegir dades individualment des del teclat, fer-ho així pot ser una mica avorrit i molest amb vista a l’execució dels programes en què s’introdueixen moltes dades, preguntant cada valor un per un i havent de pitjar la tecla de retorn cada vegada. En realitat, quan s’usa una instrucció lector.next... i el programa s’atura esperant que l’usuari introdueixi dades pel teclat, res no impedeix que, en lloc d’una única dada, aquest n’escrigui més d’una abans de pitjar la tecla de retorn. O sigui, una seqüència de valors. L’única condició és que cada valor individual estigui separat de la resta per almenys un espai, de manera que siguin fàcilment identificables. Quan això succeeix, tots els valors de la seqüència queden latents, pendents de lectura. Successives invocacions a noves instruccions de lectura automàticament aniran consumint aquests valors pendents, en lloc de causar una nova espera d’una entrada de l’usuari. Mentre quedin valors a la seqüència pendents de llegir, les instruccions de lectura mai no causaran una espera d’entrada de dades. Un cop la seqüència ha estat consumida totalment, llavors sí que la propera instrucció de lectura causarà que el programa es torni a aturar esperant una nova entrada de l’usuari. I així el cicle de lectura torna a començar.

Dins d’aquest procés hi ha una crida amb un comportament especial: lector.nextLine();. Quan aquesta crida s’invoca, automàticament es descarten tots els valors pendents de llegir de la seqüència actual. La propera crida a una instrucció de lectura sempre causarà que el programa s’aturi de nou i esperi una entrada de l’usuari. Un fet important quan es llegeixen seqüències de diversos valors escrites des del teclat és que res no impedeix que hi pugui haver valors de diferents tipus de dades barrejats. Això tant pot ser per error de l’usuari en entrar les dades, com perquè el programa realment espera una seqüència amb valors de diferents tipus intercalats. En qualsevol cas, és important que abans de fer cap lectura d’una dada es comprovi si el tipus és el que correspon usant les instruccions lector.hasNext.... Per llegir múltiples valors el més senzill és fer-ho mitjançant una estructura de repetició que vagi invocant successivament les instruccions de lectura de dades. En usar aquest mecanisme cal tenir present un fet ben important. S’ha de saber exactament quan heu obtingut ja totes les dades necessàries i cal deixar llegir. Normalment hi ha dues aproximacions depenent de si es coneix la quantitat de dades exactes que cal llegir o no.

Quantitat de dades coneguda

Si el nombre de valors que es vol llegir és conegut per endavant, per tractar la lectura de la seqüència de valors hi ha prou d’usar una estructura de repetició basada en un comptador. Aquest comptador controlarà el nombre de valors llegits i les iteracions finalitzaran en assolir el nombre de lectures que vulguem. Compileu i executeu l’exemple següent, en què es mostra com cal fer la lectura d’un seguit de valors enters. Si se n’escriuen més dels esperats, la resta es descarten. Fixeu-vos que no és necessari escriure tots els valors en una sola línia de text. Si la línia introduïda, abans de pitjar la tecla de retorn, no disposa de suficients valors, el programa s’atura esperant la resta. A més a més, aquest codi controla mitjançant una estructura de selecció si el valor que és a punt de ser llegit serà realment un enter o no.

import java.util.Scanner; public class LectorValorsConeguts { //Es llegiran 5 valors. public static void main(String[] args) { final int NUM_VALORS = 5; int dades[] = new int[NUM_VALORS]; Scanner lector = new Scanner(System.in); System.out.println("Escriu " + NUM_VALORS + " enters."); //Es llegeixen exactament NUM_VALORS valors. int numValorsLlegits = 0; while (numValorsLlegits < NUM_VALORS) { //Abans de llegir, comprovem si realment hi ha un enter. if (lector.hasNextInt()) { int valor = lector.nextInt(); dades[numValorsLlegits] = valor; numValorsLlegits++; System.out.println("Valor " + numValorsLlegits + " llegit: " + valor); } else { //Si el valor no és enter, es llegeix però s’ignora. //No s’avança tampoc el comptador. lector.next(); } } //Els valors que sobrin a la darrera línia escrita es descarten. lector.nextLine(); System.out.println("Ja s’han llegit " + NUM_VALORS + " valors."); } }

Quantitat de dades desconeguda

Un cas més complex és quan el nombre de valors no és conegut a priori, ja que pot variar en diferents execucions del programa. Quan això succeeix, una solució simple seria preguntar simplement, abans de llegir cap valor, quantes dades s’introduiran, o bé fer que el primer valor dins de la seqüència n’indiqui la longitud. Si això no és possible, o es considera que no s’ha de fer, una altra opció és establir un valor especial que no forma part de les dades per tractar dins el programa, sinó que es considerarà com a marca de final de seqüència. Tan bon punt es llegeixi aquest valor, la lectura s’ha de donar per finalitzada. La condició per poder usar aquesta estratègia és que la marca no sigui un valor que pugui aparèixer mai entre els valors introduïts. Ha de ser únic. Per exemple, dins una llista de notes, qualsevol valor negatiu o major que 10 serviria. La resta de valors no es poden usar, ja que poden correspondre a una nota real que cal tractar. Si tots els valors possibles són acceptables dins de la seqüència una opció és usar un valor d’un tipus de dada diferent. Per exemple, un programa que processa valors reals arbitraris per fer càlculs matemàtics podria usar un caràcter com a marca de fi. En aquest cas, l’estratègia per utilitzar és usar una estructura de repetició basada en semàfor. Aquest us indica si ja s’ha llegit la marca de fi i cal deixar d’iterar o no. El codi següent d’exemple llegeix una seqüència de valors enters de llargària arbitrària. Malauradament, hi ha circumstàncies sota les quals no és possible inicialitzar un array de manera que la seva mida s’ajusti exactament a la llargària de la seqüència de valors d’entrada. Hi ha dos motius que poden dur a aquesta situació. D’una banda, si simplement no es coneix a priori el nombre de dades que s’entraran, ja que en lloc del programa es pregunta prèviament, i per tant l’entrada de dades és d’una llargària desconeguda. En un cas com aquest, us haureu de conformar a triar un valor concret prefixat per a la mida de l’array i donar per fet que el nombre de dades d’entrada que el programa pot tractar és limitat. Si per algun motiu, en llegir les dades d’entrada, se supera aquest límit, com que no és possible emmagatzemar els valors excedents, els haureu d’ignorar. Per tant, caldrà triar aquesta mida arribant a un compromís entre un valor que sigui prou gran per no trobar-vos gaire sovint amb aquesta situació, però tampoc massa exagerat, per no malbaratar memòria. Per exemple, si ja sabeu que el nombre d’estudiants en una classe pot oscil·lar, però mai no es permetrà que n’hi hagi més de 80, es pot triar aquest valor com a mida de l’array. Cal estudiar cada cas En un cas com aquest, en què es disposa d’un array amb una mida que mai no serà exactament igual al nombre de dades llegides des de l’entrada (de fet, normalment, serà inferior), només hi ha un rang de posicions en què hi ha dades llegides de l’entrada. La resta de posicions tenen valors per defecte que no tenen res a veure amb l’entrada, i per tant, no han de ser previstes a l’hora de fer cap tasca. Un altre aspecte important és que l’atribut length ara no és suficient per saber el rang de les dades vàlides. Només indica la capacitat de la taula. Per tant, serà necessari disposar d’una variable auxiliar addicional, de tipus enter, en què s’emmagatzemi el nombre de dades que realment hi ha dins de la taula.

import java.util.Scanner; public class LectorValorsDesconeguts { public static void main(String[] args) { final int MARCA_FI = -1; final int MAX_VALORS = 80; int dades[] = new int[MAX_VALORS]; int elements = 0; Scanner lector = new Scanner(System.in); System.out.println("Escriu diferents valors enters. Com a màxim: " + MAX_VALORS); System.out.println("Després del darrer valor escriu un " + MARCA_FI); //Es llegeixen com a molt MAX_VALORS valors. boolean marcaTrobada = false; while (!marcaTrobada && elements < dades.length) { //Abans de llegir, comprovem si realment hi ha un enter. if (lector.hasNextInt()) { int valor = lector.nextInt(); //És la marca de fi? if (valor == MARCA_FI) { //Sí que ho és. marcaTrobada = true; } else { //No. És un valor que ha de ser tractat. dades[elements] = valor; elements++; } } else { //Si el valor no és enter, es llegeix però s’ignora. lector.next(); } } //Els valors que sobrin a la darrera línia escrita es descarten. lector.nextLine(); System.out.println("Ja s’han llegit tots els valors."); }}

Tractament seqüencial de taules

La sentència for

Recorregut de taules

La sentència for millorada

Cerca seqüencial en taules

Cerca seqüencial en taules ordenades

Taules multidimensionals

Taules bidimensionals amb files de diferents longituds

Creació de taules bidimensionals

Recorregut en taules bidimensionals

Cerca en taules bidimensionals