Föreläsningar 7,8 sept 24, sept 26 v 39).

delvis DD Chapter 6.

Metoder som returnerar värden.

När vi skriver uttryck kan vi använda inbyggda operatorer, t ex i uttrycket efter

tilldelningssymbolen i satsen :

k = 3*i + j + 4;

I uttryck också används anrop (engelska call, invocation) till metoder som finns i färdigskrivna klasser på likande sätt som i matematiken använder standardfunktioner. Så här skrivs till

exempel att variabeln x (liten del av minnet) skall innehålla värdet för sinus för 0.5 radianer och att y skall innehålla cosinus för p /4 + absolutvärdet av innehållet av x:

x = Math.sin(0.5);

y = Math.cos(Math.PI/4.0) + Math.abs(x); //Obs paranteserna!

I en matte -bok skulle det stå x = sin 0.5 och y = cos p/4 + |x|

Men hur gör man om man vill definiera egna funktioner (metoder som returnerar värden), och hur använder man dem, kanske gång på gång? Jo:

Java Matte

Definition av f:

.. class ...{

static double f(double x) { Vi definierar f(x) = x +3

return x+3;

}

Vi kallar x för (formell) parameter, i matte ofta variabel som ju betyder något helt annat i programmering!

Användning (anrop, applikation) av f

............main(.....) {

utdata.println("f(14.0) = " + f(14.0)); Vad blir f(14) ?

z = 12; Om z = 12,

u = 3.0* f(2.0*z); låt u = 3*f(2*z)

Vi kallar 14.0 och 2.0 för argument (i vissa böcker aktuell parameter).

Ytterligare ett exempel. Herons formel :

I en matte-bok kan det stå så här:

Om en triangel har sidor med längderna a, b och c kan triangelns yta beräknas med Herons formel:

area(a,b,c) = p(p-a)(p-b)(p-c)

där p = (a+b+c) / 2

Beräkna några trianglars ytor, t ex den egyptiska triangeln med sidan 3, 4 och 5.

Övning: I formeln är en sorts operator utlämnad. Vilken? Får man göra så i Java?

Detta blir i Java:

import java.io.*;

public class Heron {

static BufferedReader indata =

new BufferedReader(new InputStreamReader(System.in));

public static void main(String[] iargs) throws IOException {

System.out.println("Herons formel beräkning av trianglars ytor");

System.out.print("Ge längden för sida a : ");

double a = Double.parseDouble(indata.readLine());

System.out.print("Ge längden för sida b : ");

double b = Double.parseDouble(indata.readLine());

System.out.print("Ge längden för sida c : ");

double c = Double.parseDouble(indata.readLine());

System.out.println("Triangelns yta = " + area(a, b, c));

}

public static double area(double ia, double ib, double ic) {

double p = (ia + ib +ic) / 2.0; //Herons formula

return Math.sqrt(p*(p-ia)*(p-ib)*(p-ic));

}

}

/* Körresultat

Herons formel för beräkning av trianglars ytor

Ge längden för sida a : 3.0

Ge längden för sida b : 4.0

Ge längden för sida c : 5.0

Triangelns yta = 6.0

*/

Exemplet visar också hr man matar in en sträng från tangentbordet och hur resultatet

bifogas som en flerraders-kommentar. Detta är bekvämare än att

använda JOptionPane för inmatning. Definitonen av utdata förklaras senare.Semantik = Programmets betydelse.

1. Före main börjar köra :

När vi startar interpretern med java Heron startar main -metoden. Datorns minne

innehåller då dessa metoder och variabler :

- Klassvariablen System.out refererar till ett objekt som håller reda på skärmen och har

metoden

println(String ..) som gör att vi kan skriva på skärmen. Mer om objekt senare.

- Klassvariablen indata refererar till ett objekt som håller reda på tangetbordet och har

metoden String readLine() som gör att vi kan läsa in strängar.

- Kod för klass-metoderna void main(..) och double area(..) som hör till klassen

Heron.

- Kod för klassmetoden double parseDouble(String ..) som hör till klassen Double.

- Dessutom kommer det så länge metoden main exekverar (dvs hela tiden) finnas

en aktiveringspost (frame) för mains parameter iargs samt mains lokala variabler

a, b och c.

2. När main kört inmatningskommandona men inte gjort anropet till area :

De sju första kommandona i main fyller mains lokala variabler a, b och c med värden :

3. När main gjort anropet till area :

Minnet innehåller nu ytterligare en aktiveringspost:

- Så länge metoden area kör (exekverar) finnas en aktiveringspost (frame) för areas parametrar

ia, ib, och ic samt areas lokala variabel p.

Parmetrarna får initialvärden som är lika med de värden som argumenten i anropet har:

System.out.println("Triangelns yta = " + area(a, b, c)); // i main

public static double area(double ia, double ib, double ic) {

Första argumentet är uttrycket a som har värdet 3.0 (innehållet i a just nu) och detta värde blir initialvärdet för parametern ia . Pss fylls parametern ib med värdet i variabeln b och ic värdet i c. Parametrarna fungerar i fortsättningen som lokala variabler.

4. När area gjort tilldelningen till p :

Metoden area kör nu igång coh beräknar lokala variablen p till ( 3.0 + 4.0 + 5.0 )/2.0 = 6.0.

Metoden areas aktiveringspost ser nu ut så här :

5. När area kört klart och main kör sitt sista kommando :

Det sista metoden area gör är att beräkna sitt retur värde och lämnar detta resultat i CPU:n.

Metoden areas aktiveringspost försvinner.

Metoden main fortsätter nu med att köra sitt System.out.println -kommando, som avbröts av anropet. Vid beräkning av println -kommandos argument,

"Triangelns yta = " + area(a,b,c), kan resultatet av anropet till area,

som finns i CPU- användas:

Metoder som returnerar intet (void ).

När vi skriver uttryck, i t ex tilldelningar och anropsuttryck, har vi alltså glädje av metoder som returnerar värden, speciellt om vi på flera ställen i ett program behöver samma metod.

Om vi på flera ställen i ett program har några kommandon som i stort sett gör samma sak

skulle det vara bra att ha ett egentillverkat "super"-kommado som vi kunde använda på dessa

ställen.

Exempelvis om vi i ett spel-program på flera platser har skrivit dessa satser:

System.out.print("Ställning : ");

for (int i = 0; i <= talNu; i = i+1) {

System.out.print(" " + i);

}

System.out.println();

Vi kan nu i stället skriva (på alla platser där ställningen ska skrivas ut):

tryckStällning(talNu);

om vi definierar en ny metod void tryckStällning(int ital) på detta sätt:

public static void skrivStällning(int ital){

System.out.print("Ställning : ");

for (int i = 0; i <= ital; i = i+1) {

System.out.print(" " + i);

}

System.out.println();

}

Även void-metoder kan definieras med parametrar och anropas med argument p s s sätt

som returnerande metoder. Det fungerar på samma sätt med en ny aktiveringspost. Aktiveringsposten för void skrivStällning(int ii) har alltså en en parameter för parametern ital . I blocket för for-satsen tillkommer en "låda" för blockets lokala variabel i.

Användning av metoder.

Att definiera och använda metoder krävs praktiskt taget så fort ett program blir någotsånär stort. Om man skriver sina nya metoder generella kan man t o m ha glädje av dem i andra sammanhang. De förtjänar då att ligga i egna klasser och förklaras till public.

Man kan t o m ha glädje av andras klass-metoder från färdigskrivna och färdigkompilerade klasser. Det är detta vi gör med t ex anropet Double.parseDouble.

Anropen skrivs som <Klassnamn>.<metodnamn>(<argumentlista>). Ev krävs import.

Partiella funktioner och enkel rekursion.

Vi definierar i matte

0! = 1

n! = (n-1)! * n för n >0

dvs 5! =

4!*5 =

(3!*4)*5 =

((2!*3)*4)*5 =

(((1!*2)*3)*4)*5 =

((((0!*1)*2)*3)*4)*5 =

((((1*1)*2)*3)*4)*5 =

(((1*2)*3)*4)*5 =

((2*3)*4)*5 =

(6*4)*5 =

24*5 =

120

men man kan också räkna så här

n! = 1*2* ... *n

dvs 5! = 1*2*3*4*5 = ... 120

Ett program som använder båda sätten att tänka :

import java.io.*;

public class TestFak {

static BufferedReader indata =

new BufferedReader(new InputStreamReader(System.in));

public static void main( String args[] ) throws IOException {

//fixa för första varv

System.out.print("Ge ett värde n (slute ge <0 ): ");

int n = Integer.parseInt(indata.readLine());

while (n >= 0) {

//gör det viktiga

System.out.println(" n ! (Iterativt) = " + fakIter(n));

System.out.println(" n ! (Rec) = " + fakRec(n));

//fixa för ev nytt varv

System.out.print("Ge ett värde n (slute ge <0 ): ");

n = Integer.parseInt(indata.readLine());

}

}

// retunerar noll för negativa n

public static int fakIter(int n) {

int result = 1;

for (int i = 0; i < n; i = i+1) {

result = result*(i+1);

}

return result;

}

// avbryter programmet för negativa n

public static int fakRec(int n) {

if (n < 0) {

throw new RuntimeException("Tried to do n! for n <0");

} else if (n == 0 ) {

return 1;

} else {

return fakRec(n-1)*n;

}

}

}

De lokala variblerna i den enda aktiveringsposten i fakIter(int n)

vid körning av for-snurran:

De lokal variablen (parametern n) i aktiveringsposterna och returvärdet i ALU:n

i fakRec(int n) vid den rekursiva körningen:

Dubbel rekursion.

Ett program som gör två rekursiva anrop:

import java.awt.*;

import javax.swing.*;

public class DrawTree extends JApplet {

private final int startmodul = 30;

public void paint(Graphics g) {

drawTree(g, 500, 10, 0, 30*startmodul);

}

private void drawTree(Graphics g, int i, int j,

int niv, int storlek) {

int modul = storlek/30;

if (niv == 5) {

g.drawOval(i-5*modul, j-5*modul, 8 , 8);

} else {

g.drawLine(i, j, i + 8*modul, j + 30);

g.drawLine(i, j, i - 8*modul, j + 30);

drawTree(g, i + 8*modul, j+30, niv+1, 15*modul);

drawTree(g, i - 8*modul, j+30, niv+1, 15*modul);

}

}

}

Program av detta slag är svåra att skriva utan rekursion.

Körresultat: