Algorithmen und Programmierung II

Übungsblatt 5 - Musterlösung


Aufgabe 1

a)


 i
 j
 k
 q:
b

 i


 Ausgabe 

0
0
0







i=0;










j=1;

1








k=2;


2







q(true,k);




true





   int i=j;






2



   p(j);
 
 
 



 



      i++;

3







      show(i,j,k);









3 1 3
   show(i,j,k);









2 3 3
q(false, i);




false





   int i=j;






0



   p(i);










      i++;






1



      show(i,j,k)









1 1 3
   show(i,j,k)









1 0 3

b)


 i
 j
 k
 q:
b
 j
 i
 p:
 i
 Ausgabe 

0
0
0







i=0;










j=1;

1








k=2;


2







q(true,k);




true
0




   int i=j;






0



   p(j);
 
 
 





0

      i++;







1

      show(i,j,k);









1 1 2
      <return>





1




   show(i,j,k);









0 1 2
   <return>

1







q(false, i);




false
0




   int i=j;






0



   p(i);








0

      i++;








1

      show(i,j,k)









1 1 1
      <return>





1



   show(i,j,k)









1 0 1
   <return>











Aufgabe 2

a)

int (int, int, int(int, int))

b)

Der Rumpf besteht aus einer return-Anweisung. Zu überprüfen ist, ob der zurückgelieferte Wert vom Typ int ist. Der Ausdruck hinter return ist ein bedingter Ausdruck. Desser erster Teilausdruck "x==0" ist vom Typ boolean (Ergebnis eines Vergleichsoperators). Der zweite Teilausdruck "n" besteht aus dem Parameter n der Methode a und ist vom Typ int. Der letzte Teilausdruck "f(x, a(n,x-1,f))" stellt den Aufruf der als Parameter f übergebenen Methode dar. f hat den Typ int(int,int). Als erster Parameter wird x vom Typ int übergeben. Der Methodenaufruf von a bildet den zweiten Parameter. Die Typen der aktuellen Parameter von a sind korrekt (x vom Typ int, x-1 vom Typ int und f vom Methodentyp int(int,int)). Der Rückgabewert von a ist vom Typ int und damit korrekter zweiter Parameter von f. Der Rückgabewert von f hat Typ int. Mit diesen zweiten und dritten Parametern hat auch der bedingte Ausdruck den Typ int und passt zum Typ des Rückgabewerts von a.

c)

a bewirkt eine Faltungsoperation auf der Zahlen 1, 2, ..., x mit der Operation f und dem neutralen Element n bezüglich f. Sei folgende Methode mult definiert:
static int mult(int a, int b) { return a*b; }
Dann setzt sich die Auswertung des Aufrufs von a(1, 4, mult) wie folgt zusammen:
a(1, 4, mult) =
mult(4, a(1, 3, mult)) = 4*(3*(2*(1*1))) = 4! = 24
mult(3, a(1, 2, mult)) = 3*(2*(1*1))
  mult(2, a(1, 1, mult)) = 2*(1*1)
mult(1, a(1, 0, mult)) = 1*1
1
d)

a in Haskell (monomorph):
a :: Int -> Int -> (Int -> Int -> Int) -> Int
a n 0 f = n
a n x f = f x (a n (x-1) f)
Als Funktionsparameter f könnten z.B. die folgende Funktion übergeben werden:
f :: Int -> Int -> Int
f = (*)
Typ von a in Haskell (polymorph):
a :: Num t => b -> t -> (t -> b -> b) -> b
Das zweite Argument muss einen numerischen Typ haben, da es mit 0 verglichen wird und in dem arithmetischen Ausdruck "x-1" verwendet wird.

Funktionsparameter:
f :: Num t => t -> b -> b

Aufgabe 3

a)
class Complex {

float re, im;
final static Complex I = new Complex(0, 1);

Complex(float r, float i) {
re = r;
im = i;
}
void add(Complex c) {
re += c.re;
im += c.im;
}
void sub(Complex c) {
re -= c.re;
im -= c.im;
}
void mul(Complex c) {
float new_re = re*c.re - im*c.im;
im = re*c.im + im*c.re;
re = new_re;
}
void div(Complex c) {
float new_re = (re*c.re + im*c.im) / (c.re*c.re + c.im*c.im);
im = (im*c.re - re*c.im) / (c.re*c.re + c.im*c.im);
re = new_re;
}
float abs() {
return (float) Math.sqrt(re*re + im*im);
}
boolean equ(Complex c) {
return re == c.re && im == c.im;
}
}
b)
class Cmplx {

final float re, im;
final static Cmplx I = new Cmplx(0, 1);

Cmplx(float r, float i) {
re = r;
im = i;
}
Cmplx add(Cmplx c) {
return new Cmplx(re + c.re, im + c.im);
}
Cmplx sub(Cmplx c) {
return new Cmplx(re - c.re, im - c.im);
}
}
c)

Die erste Variante kommt dem Gedanken der Objektorientierung näher: die Operationen modifizieren das Objekt. Die zweite Variante entspricht eher der mathematischen Sichtweise: Die Verknüpfung von zwei Operanden liefert ein Ergebnis, hier in Form eines neuen Objekts. Die Operanden bleiben dabei unverändert und stehen auch noch nach Ausführung der Operation zur Verfügung. Gibt es in einem Programm mehrere Verweise auf ein Complex-Objekt, so zeigen nach Ausführung einer Methode alle Verweise auf das modifizierte Objekt. Bei Verwendung von Cmplx würden die Verweise auf das unverändert alte Objekt zeigen. Wegen ihrer Nähe zu den herkömmlichen numerischen Typen würde man bei komplexen Zahlen vielleicht eher das letzte Verhalten bevorzugen.

Da die Objekte bei Variante b) unverändert bleiben, würde es sich auch anbieten, die Operationen als statische Methoden anzulegen, etwa static Cmplx add(Cmplx a, Cmplx b).