Fält - indexerad variabel
Problemställning
Vid större program har man oftast behov av många variabler med samma syfte. Till exempel flera objekt som alla har sina x och y koordinater. Vi kunde skapa dessa med int x1, x2, x3, x4, .... med det blir ohanterligt i längden. Det vi behöver är ett variabel variabelnamn. Ett namn som är en kombinationa av ett namn (=bokstäver) och en siffra. För att använda en annanbild: Vi kommer att skapa en 'stor' variabel med namnet tal men instället för ett värde kan den håller ett större antal värden av samma typ. Tänk en låda tal som har flera fack. I varje fack kan du lagra ett tal.
Loopen som iterar genom alla fältelement kan så klar göras med en while-sats eller en for sats. Här kommer kodexemplet med både och. while och for har i det här fallaet samma beteende.
Kodexempel
import java.util.Random;
//Variabler
int[] tal = new int[20]; // skapa lådan (=fält) 'tal' med 20 fack
Random rnd = new Random();
void setup(){
size(620,600);
int i = 0;
while( i < tal.length ){
int slmp = rnd.nextInt(400)+50; // generera slumptal 50 .... 450 och spara i slmp
tal[i] = slmp; // och spara i slmp
i++;
}
for(int j = 0; j < tal.length; j++){ //loopar genom fältet
int pos = 10+j*30;
rect(pos, 10, 20, tal[j] ); // och använda tal[i] för rektangelns höjd
}
}
För att förmedla tankesättet kommer här pseudokoden:
Pseudokod
i = 0
Sålänge i mindre fältets storlek
Generera ett slumptal slmp
Lagra slmp i tali
höjd i med 1
i = 0
x = 10
Sålänge i mindre fältets storlek
rita stapel på position x, höjden = tali
höj i med 1
höj x med 20
Detaljer
Observera att första elementet har numreringen noll (0), sista 19. På samma sätt som i ändrar sig 0 ... 19
tal.length motsvarar alltid antal element i fältet. Om man gör så här behöver man bra ändra int[] tal = new int[20] till int[] tal = new int[30] och allt fungera ändå!
Fältets storlek bestäms när den skapas ( int[] tal = new int[20]; ) . Det kan inte utökas i efterhand.
tal är ett fält (array) av int-variabler. Och då är varje tal[i] är i sin tur en int-variabel.
Så tal[0]++ eller tal[i] += 10 fungerar!
Array/fält är så klart inte begränsat till int. Det finns array för varje typ af variabel:
double[] decis = new double[10]; // index/numrering 0...9
float[] floatis = new float[1000];
String[] texter = new String[]; //(för text)
boolean[] skaRitas = new boolean[200];
int[] tal = {15,35,46,58,160,221,41,152,16};
Sista raden: Man kan deklarera och initialisera (=fylla med värden) på en gång. Kompilatorn känner av att det är 9 tal och skapar ett motsvarande array/fält.
Laboration 1
1 Flytta andra loopen till draw()-blocket. De värden i tal (tal[0], tal[1], ....) används som höjd för varje stapel. Se till att staplarna blir mindre och mindre med varje omgång av draw(). Du måste, i en loop, minskar varje tal[i].
2 Vrid hela diagram från hängande staplar till stående eller liggande. Använd funktioner som ändra koordinatsystemet eller rita staplarna på ett annat sätt. Använd i så fall tali för rektanglens bredd och ändra y-koordinaten för rektangeln med 20 varje gång.
3 Skriv ett statisktikprogram. Fråga användaren efter tio tal och visa de som ett stapeldiagram. Här är pseudokoden som beskriver programmet:
Pseudokod
i = 0Sålänge i mindre 10
Fråga efter ett tal
Lagra det i tali
höjd i med 1
i = 0
x = 10
Sålänge i mindre 10
rita stapel på position x, höjden = tali
höj i med 1
höj x med 20
Tips Programmet ska ju testas många gånger. Att varje omgång mata in 10 tal är jobbigt. När du har inläsningsloopen klar och vill fortsätter kan du (för testandet) istället deklarera och initialisera ett fält. Kom ihåg: Du kan avaktivera enstaka kodrader med // framför raden. ett helt block med / i början och / i slutet. Din kod kan sedan se ut så här:
// int[] tal = new int[20];
int[] tal = {15,35,46,58,160,221,41,152,16};
/*
while( .....
} // end while
*/
for( int i = 0; .....
4 Börja om med koden från första exemplet. Nu ska du markera största talet/längsta stapel med en egen färg (Du måste naturligtvis utgår ifrån att du inte vet det i förväg, även om du testar din kod med ett fördefinerad fält!). Programmet kommer består av tre loopar (iterationer)
- Fyll fället med slumptal (finns redan i exemplet, första loop)
- Hitta största tal (hitta på själv)
- Rita staplarna med särskild färg för den största/längsta. (finns delvis, är andra loopen, ska utökas med färgvalet)
loop 2 och 3 i pseudokod:
Pseudokod
// hitta största talmax = // Vad borde sättas här?
i = 0
Sålänge i mindre 10
om tali är större än max
max = tali
höj i med 1
// för att sedan rita alla staplar, en med särskild färg
i = 0
x = 10
Sålänge i mindre 10
Om taltali = max
sätt färg på grön
annars sätt färg på vit
rita stapel på position x, höjden = tali
höj i med 1
höj x med 20
5 Markera nu också den minsta stapeln.
6 Skriv ut alla värden under stapeln.
7 Beräkna även summan av alla tal och medelvärdet. Visa även dessa två värden på skärmen. Medelvärdet kanske som linje över alla staplar?
Mus med array
I spel har man oftast flera objekt och då är det lämpligt att håller dessa informationer i en array. Ett enkel exempel hur man fyller två fält (posX, posY) med slumptal för positionen. I draw()-blocket rita cirklar med hälp av denna information:
import java.util.Random;
Random rnd = new Random();
int FAELTSIZE = 20;
int[] posX = new int[FAELTSIZE];
int[] posY = new int[FAELTSIZE];
int radie = 25;
void setup(){
size(800,800);
// fyll fält med slumptal
for(int j = 0; j < posX.length; j++){
posX[j] = rnd.nextInt(700)+50;
posY[j] = rnd.nextInt(700)+50;
}
}
void draw(){
background(0);
//Rita alla cirklar
for(int j = 0; j < FAELTSIZE; j++){
ellipse(posX[j],posY[j],2*radie,2*radie); //med radie 25
}
}
Inget nytt här, det hade vi klarat utan fält. Men vi vill kunna klicka på en cirkel för att markera den. Vi behöver en extra variabel (int markedCircle = -1;) som håller den informationen. Vilken som klicktes på avgörs i mousePressed()-blocket. Med funktionen dist(float,float,float,float) kan vi bestämma avstånd mellan två punkter. I en loop beräknas avstånd för varje cirkel. om den är mindre än radien sparas platsnumret i markedCircle.
import java.util.Random;
Random rnd = new Random();
int FAELTSIZE = 20;
int[] posX = new int[FAELTSIZE];
int[] posY = new int[FAELTSIZE];
int markedCircle = -1; // -1 för att ingen ska vara markerad från början.
int radie = 25;
void setup(){
size(800,800);
// fyll fält med slumptal
for(int j = 0; j < posX.length; j++){
posX[j] = rnd.nextInt(700)+50;
posY[j] = rnd.nextInt(700)+50;
}
}
void draw(){
background(0);
//Rita alla cirklar
for(int j = 0; j < FAELTSIZE; j++){ // FAELTSIZE som alternativ till posX.length
if(markedCircle == j ) { fill(0,255,0); }
else { fill(255); }
ellipse(posX[j],posY[j],2*radie,2*radie); //med radie 25
}
}
// dist mäta avstånd mellan två punkter (x1,y1) till (x2,y2)
// med Pytagoras sats, kolla Reference
void mousePressed(){
for(int j = 0; j < FAELTSIZE; j++){
float d = dist( posX[j] , posY[j] , mouseX , mouseY ); //avstånd cirkelns mittpunkt och musen.
if(d < radie) { markedCircle = j;}
}
}
Info
Kolla Reference med högerklick på dist --> Find in Reference
Laboration 2
1 Nu gäller det att inte bara kunna markera en. Lägg till ett extra fält ( tex boolean[] new marked = boolean[FAELTSIZE]; ) (Minnesbild). for-satsen i mousePressed()-blocket kan redan nu avgöra vilken cirkel det har klickts på. motsvarande fältelement i marked kan sedan sättas på true: ( på rätt ställe: marked[j] = true; ) . I for-satsen som rita alla cirklar kan man sedan använda informationen från marked för att avgöra färgen: ( if marked[j] ) {....} ).
2 Inför knappkommando: När du trycker på r så raderas alla markeringar, med a markeras alla. Kolla Interaktion hur det var med knapptryckshändelser.
3 Med varje klick ska det försvinna en cirkel. Du kan göra så här att du sätter koordinaterna för rätt cirkel på (-100,-100) så att den ritas utanför fönstret och är därmed osynligt. Inte särskild effektiv med tanken att de utanför fönstret är ju ändå med i beräkningen och alla iterationer. Som utmaning kan du fundera på hur det borde göras!
foreach
För fält finns ett speciellt sätt att formulera en for-sats ( tagen från första kodexempel):
//vanligt
for(int j = 0; j < tal.length; j++){ //loopar genom fältet
translate(30,0);
rect(10, 10, 20, tal[j] ); // och använda tal[i] för rektangelns höjd
}
//nytt
for(int element : tal){ //loopar genom fältet
translate(30,0);
rect(10, 10, 20, element ); // och använda tal[i] för rektangelns höjd
}
I andra for-satsen tas tal0, överförs till element och koden mellan måsvingarna utförs. Sedan överförs tal1 element och koden körs, sedan tal2 ... fram till sista värdet i fältet. I båda for-satser händer precis samma sak.
- Fördel: Enklare skrivsätt, ännu mer kompakt, behövs inga funderingar om antal när man ändå vi ha alla fältelement med i loopen.
- Nackdel: det finns ingen sk 'löpvariabel' ( här j) som kan användas för tex beräkningar av koordinater ( Se första exemplet). Bara användbar för att läsa fältelement (just nu) , inte skriva. Det här tex fungera inte för att ett tal kopieras från tal till t :
for(int t : tal ){
int slmp = rnd.nextInt(400)+50; // generera slumptal 50 .... 450 och spara i slmp
t = slmp;
}
Övning
Ta en av övningar och gör om vanliga for-satser till den nya formen.
fler exempel:
Fältdeklaration | Passande for-sats |
---|---|
float[] vinkel = new float[20]; | for(float v : vinkel) { .......} |
double[] parameter = new double[20]; | for(double prm : parameter) { .......} |
String[] namn = new String[100]; | for(String n : namn) { .......} |
Knäcka isär en sträng
När program koomunicera med andra program överförs data oftast i ett enda flöde (stream) från sändaren till mottagren. Kolla till exempel en internet länk till en produktsida.
https://www.amazon.de/Inakustik-High-End-Netzwerkkabel-CAT7-L%C3%A4nge/dp/B0088B0D8M/ref=pd_sbs_147_8?_encoding=UTF8&pd_rd_i=B0088B0D8M&pd_rd_r=572fbd00-2941-11e9-9554-5bcb7413d47b&pd_rd_w=KgwuQ&pd_rd_wg=6JNQ5&pf_rd_p=74d946ea-18de-4443-bed6-d8837f922070&pf_rd_r=5A436ZM4MX1CAK296XQT&psc=1&refRID=5A436ZM4MX1CAK296XQT
Der här skickas till servern (amzon.de). Och prgrammet måste sedan plockar isär denna teckenföljd ( = String ) för att komma åt alla informationsbitar. Det finns även sådana funktioner i processing/java:
//Variabler
void setup(){
String allaParameter = "_encoding=UTF8&pd_rd_i=B0088B0D8M&pd_rd_r=572fbd00-2941-11e9-9554-5bcb7413d47b&pd_rd_w=KgwuQ&pd_rd_wg=6JNQ5&pf_rd_p=74d946ea-18de-4443-bed6-d8837f922070&pf_rd_r=5A436ZM4MX1CAK296XQT&psc=1&refRID=5A436ZM4MX1CAK296XQT";
String[] paras = allaParameter.split("&");
println("Strängen har " + paras.length + " parameterpar:");
for(int i = 0; i < paras.length;i++){
println(paras[i]);
}
println("\n");
//eftersom vi vet att varje para har två delar:
for(int i = 0; i < paras.length;i++){
String[] rader = paras[i].split("=");
String nyckel = rader[0];
String verde = rader[1];
println( nyckel + "\t---> " + verde);
}
}
Det avgörande raderna är String[] paras = allaParameter.split("&");
. Här tas strängen allaParameter och splittras och överföras till ett fält av strängar (paras); Skiljetecken är '&' och den försvinner under processen. I String[] rader = paras[i].split("=");
händer samma sak. paras[i] är en sträng som i sin tur kan splittar med '=' .
Sammanfattning
Sammanfattning
- Ett fält är en speciell variabelform som tillåter indexering
- Index börja alltid vid 0
- Deklaration som vanligt men [] och new kommer till: int[] tal = new int[100];
- Det finns ett speciellt sätt att formulera en for-sats i samband med fält om man ändå ska itera (går genom) alla element i fältet.