fakult¨at f¨ur informatik - Sprachen und Beschreibungsstrukturen

Transcription

fakult¨at f¨ur informatik - Sprachen und Beschreibungsstrukturen
TECHNISCHE UNIVERSITÄT MÜNCHEN
FAKULTÄT FÜR INFORMATIK
Lehrstuhl für Sprachen und Beschreibungsstrukturen
Einführung in die Informatik I
Prof. Dr. Helmut Seidl, A. Lehmann, A. Herz, Dr. M. Petter
SS 2011
Übungsblatt 11
14.07.11
Abgabe: 24.07.11 (vor 12 Uhr)
Aufgabe 11.1 (P) Generische doppelt-verkettete Listen
Laden Sie von der Übungsseite die Klasse DoublyLinkedStringList herunter. Ändern Sie
die Klassen DoublyLinkedStringList bzw. ListElement so ab, dass ein generischer Typparameter T für das info-Attribut verwendet wird. Achten Sie darauf, sowohl keine Typecasts
einzubauen als auch die Typparameter so präzise wie möglich zu verwenden. Sie sollten dazu
in NetBeans unter Tools/Options/Editor/Hints Standard Javac Warnings einschalten.
Lösungsvorschlag 11.1
package g e n e r i c ;
f i n a l c l a s s L is tElem ent <E> {
E info ;
ListElem ent <E> prev ;
ListElem ent <E> next ;
@SuppressWarnings ( ” L e a k i n g T h i s I n C o n s t r u c t o r ”)
L i s t E l e m e n t (E i n f o ) {
this . i n f o = i n f o ;
prev = next = t h i s ;
}
L i s t E l e m e n t (E
this . i n f o
t h i s . prev
t h i s . next
}
i n f o , L istElement <E> prev , Lis tElem ent <E> next ) {
= info ;
= prev ;
= next ;
@Override
public S t r i n g t o S t r i n g ( ) {
return i n f o . t o S t r i n g ( ) ;
}
}
public c l a s s DoublyLinkedList <T>
{
/∗ ∗
∗ The s e n t i n e l e l e m e n t i s used f o r e a s y i d e n t i f i c a t i o n o f t h e
beginning
∗ and end o f t h i s d o u b l y l i n k e d l i s t .
∗/
f i n a l L is tElem ent <T> s e n t i n e l = new L is tElem ent <T>( null ) ;
/∗ ∗
∗ Auxiliary function that i n s e r t s the given T before the given
∗ FooBar .
2
∗ @return
a r e f e r e n c e t o t h e newly c r e a t e d FooBar .
∗/
ListElem ent <T> i n s e r t (T someT , L istEle ment <T> e l t ) {
Lis tElem ent <T> newElt = new L is tElem ent <T>(someT , e l t . prev , e l t
);
newElt . prev . next = newElt ;
newElt . next . prev = newElt ;
return newElt ;
}
/∗ ∗
∗ Appends t h e g i v e n T t o t h e end o f t h i s l i s t .
∗/
public void append (T someT ) {
i n s e r t ( someT , s e n t i n e l ) ;
}
/∗ ∗
∗ Prepends t h e g i v e n T t o t h e b e g i n n i n g o f t h i s l i s t .
∗/
public void prepend (T someT ) {
i n s e r t ( someT , s e n t i n e l . next ) ;
}
/∗ ∗
∗ Removes t h e g i v e n L i s t E l e m e n t from t h i s l i s t .
∗/
void remove ( L istElement <T> e l t ) {
// Ensure t h a t t h i s l i s t c o n t a i n s t h e g i v e n L i s t E l e m e n t .
Lis tElem ent <T> c u r ;
fo r ( c u r = s e n t i n e l . next ; c u r != s e n t i n e l ; c u r = c u r . next ) {
i f ( c u r == e l t )
break ;
}
i f ( c u r != s e n t i n e l ) {
c u r . prev . next = c u r . next ;
c u r . next . prev = c u r . prev ;
}
}
/∗ ∗
∗ A comma−s e p a r a t e d s t r i n g r e p r e s e n t a t i o n o f t h i s l i s t .
∗/
@Override
public S t r i n g t o S t r i n g ( ) {
S t r i n g B u i l d e r sb = new S t r i n g B u i l d e r ( ) ;
fo r ( ListEleme nt <T> e l t = s e n t i n e l . next ; e l t != s e n t i n e l ; e l t =
e l t . next ) {
sb . append ( e l t ) ;
i f ( e l t . next != s e n t i n e l )
sb . append ( ” , ”) ;
}
return sb . t o S t r i n g ( ) ;
}
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
S t r i n g [ ] names = {
3
”Helga ” , ”Hans ” , ”Edith ” , ”Uwe” , ”Margot ” , ”Ernst ” , ”
Irmgard ” , ”Otto ” ,
”Gerhard ” , ” I l s e ” , ” S i e g f r i e d ” , ”Gertrud ” , ” F r i e d r i c h ” , ”
Rosemarie ”
};
DoublyLinkedList <S t r i n g > l s t = new DoublyLinkedList <S t r i n g >() ;
fo r ( S t r i n g name : names )
l s t . append ( name ) ;
System . out . p r i n t l n ( l s t ) ;
}
}
Aufgabe 11.2 (P) Generisches Programmieren
Diskutieren Sie folgenden Java-Code:
i n t e r f a c e I<T> {
T
h() ;
}
c l a s s A implements I<A> {
public A h ( ) { return t h i s ; }
}
c l a s s B extends A{ }
c l a s s C <T> {
T
f (T t ) { return t ; }
<U> U g (U u ) { return u ; }
}
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
A a
= new A( ) ;
B b
= new B( ) ;
C<A>ca = new C<A>() ;
a
a
b
a
a
b
=
=
=
=
=
=
ca . f ( a ) ;
ca . f ( b ) ;
ca . f ( b ) ;
ca . g ( a ) ;
ca . g ( b ) ;
ca . g ( b ) ;
//
//
//
//
//
//
1
2
3
4
5
6
ca
C<B>cb
a
b
a
=
=
=
=
=
new C<B>() ;
new C<B>() ;
cb . f ( a ) ;
cb . f ( b ) ;
cb . g ( a ) ;
//
7
}
c l a s s D <T extends A> {
T
f (T t ) { return t . h ( ) ; }
}
c l a s s E <T extends I> {
T
f (T t ) { return t . h ( ) ; }
}
c l a s s G <T> extends E <T> {
T
f (T t ) { return t . h ( ) ; }
}
c l a s s H <T extends I<T> > extends E <T>
T
f (T t ) { return t . h ( ) ; }
}
// 8
// 9
// 10
// 11
// 12
// 13
{
// 14
4
Lösungsvorschlag 11.2
Kommentarnummer
3: Der Typparameter von ca ist auf A festgelegt, daher ist f() zu allgemein um an b
zugewiesen zu werden.
Lösungsvorschlag: Typecast auf B oder Realisierung analog zu g()
7: Die Typen C<A> und C<B> sind einander nicht zuweisbar. Die verschiedenen Instanziierungen des Typparameters machen die beiden Typen zu verschiedenen Typen. Die
Vererbungsbeziehung zwischen den Typparametern alleine nutzt nichts, Typparameter unterstützen keine Polymorphie.
Lösungsvorschlag: Keiner, ist ein Konzeptproblem.
8: C<B> instantiiert den Typparameter T mit B, so dass f seinen Parameter vom vom Typ
B erwartet. Daher klappt die Zeile so wie sie darsteht nicht.
Lösungsvorschlag: Abhilfe schafft hier nur der Typecast cb = cb.f((B)a);, der ausnutzt, dass wir unter 2 die Variable a mit b gefüllt haben.
11: Im ersten Moment sieht diese Zeile richtig aus, funktioniert aber nicht, weil der Rückgabetyp von h() in A auf A fixiert wird, statt variabel zu bleiben, wie es in D gefordert
wird. Sobald man D z.B. als D<B> instantiiert kommt es zu einem Problem!
12: Wenn man generische Typen nimmt um Typvariablen zu instantiieren sollte man aufpassen, dass man auch deren Typparameter beachtet, sonst wird so wie hier als Default
für den Parameter T in I<T> der allgemeinste Typ Object gewählt - mit der Folge dass
h() nicht mehr den Anforderungen für den Rückgabetypen genügen kann.
Lösungsvorschlag: T extends I<T>
13: Prinzipiell nett gedacht, aber wieder verkehrt: Die Verwendung von E als Basisklasse
für G soll hier sicherstellen, dass h() den richtigen Rückgabetyp hat. Dazu hätte allerdings G den Typparameter schon einschränken müssen.
Lösungsvorschlag: siehe Punkt 14
14: Korrekt. Dieses Konstrukt sieht abstrakt und total künstlich aus, wird aber z.B. auf
diesem Blatt bei für die Comparable-Listen verwendet.
Aufgabe 11.3 (P) Iterator
Das Interface java.util.Iterator aus der Java-API ist ein generisches Interface, um sequentiell auf eine Sammlung von Objekten zuzugreifen. Die Klasse DoublyLinkedList aus
der Aufgabe 11.1 soll nun um dieses Interface erweitert werden:
• Programmieren Sie die generische Klasse DoublyLinkedListIterator, die das generische Interface Iterator implementiert und mit Hilfe der Methoden hasNext(),
next() sowie remove() über die info-Attribute der Elemente einer doppelt-verketteten
Liste iterieren kann.
• Leiten Sie eine neue generische Klasse IterableDoublyLinkedList von Ihrer generischen Implementierung der Klasse DoublyLinkedList ab, die das generische Interface
Iterable implementiert, dessen Methode iterator() jeweils eine neue Instanz von
DoublyLinkedListIterator zurückgibt.
Durch diese Modifikation können Sie nun u.a. auch mit Hilfe der folgenden erweiterten Variante der for-Schleife über die Elemente Ihrer Liste iterieren:
I t e r a b l e D o u b l y L i n k e d L i s t <I n t e g e r > l s t =
/∗ C r ea t e and f i l l a new l i s t o f I n t e g e r s . ∗/ ;
for ( I n t e g e r i : l s t ) {
System . out . p r i n t l n ( i ) ;
}
5
Lösungsvorschlag 11.3
Die Klasse DoublyLinkedListIterator:
package g e n e r i c ;
import j a v a . u t i l . I t e r a t o r ;
public c l a s s D o u b l y L i n k e d L i s t I t e r a t o r <T> implements I t e r a t o r <T> {
private DoublyLinkedList <T> l i s t ;
private L is tElem ent <T> c u r r e n t ;
public D o u b l y L i n k e d L i s t I t e r a t o r ( DoublyLinkedList <T> l i s t ) {
this . l i s t = l i s t ;
this . current = l i s t . s e n t i n e l ;
}
/∗ ∗
∗ Returns t r u e i f t h e i t e r a t i o n has more e l e m e n t s .
∗/
@Override
public boolean hasNext ( ) {
return c u r r e n t . next != l i s t . s e n t i n e l ;
}
/∗ ∗
∗ Returns t h e n e x t e l e m e n t i n t h e i t e r a t i o n .
∗/
@Override
public T next ( ) {
i f ( c u r r e n t . next == l i s t . s e n t i n e l )
return null ;
c u r r e n t = c u r r e n t . next ;
return c u r r e n t . i n f o ;
}
/∗ ∗
∗ Removes from t h e u n d e r l y i n g c o l l e c t i o n t h e l a s t e l e m e n t r e t u r n e d
∗ by t h e i t e r a t o r .
∗/
@Override
public void remove ( ) {
l i s t . remove ( c u r r e n t ) ;
}
}
Die Klasse IterableDoublyLinkedList:
package g e n e r i c ;
import j a v a . u t i l . I t e r a t o r ;
public c l a s s I t e r a b l e D o u b l y L i n k e d L i s t <T> extends DoublyLinkedList <T>
implements I t e r a b l e <T>
{
6
/∗ ∗
∗ Returns an i t e r a t o r o v e r a s e t o f e l e m e n t s o f t y p e T.
∗/
@Override
public I t e r a t o r <T> i t e r a t o r ( ) {
return new D o u b l y L i n k e d L i s t I t e r a t o r <T>( t h i s ) ;
}
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
I t e r a b l e D o u b l y L i n k e d L i s t <I n t e g e r > l s t = new
I t e r a b l e D o u b l y L i n k e d L i s t <I n t e g e r >() ;
fo r ( int i = 0 ; i < 2 5 ; i ++)
l s t . append ( i ) ;
System . out . p r i n t l n ( l s t ) ;
System . out . p r i n t l n ( ”−−−”) ;
// I t e r a t e o v e r t h e l i s t and remove a l l even e l e m e n t s .
I t e r a t o r <I n t e g e r > i t = l s t . i t e r a t o r ( ) ;
while ( i t . hasNext ( ) ) {
I n t e g e r i = i t . next ( ) ;
i f ( i . i n t V a l u e ( ) % 2 == 0 )
i t . remove ( ) ;
}
// I t e r a t e o v e r t h e l i s t and p r i n t a l l e l e m e n t s , t h i s time by
// means o f u s i n g Java ’ s e x t e n d e d f o r s y n t a x .
fo r ( I n t e g e r i : l s t )
System . out . p r i n t l n ( i ) ;
}
}
Aufgabe 11.4 (P) Comparable
Das Interface java.lang.Comparable aus der Java-API ist ein generisches Interface, das es
ermöglicht, Vergleiche auf beliebigen Objekten durchzuführen. Für die Klasse IterableDoublyLinkedList aus Aufgabe 11.?? soll nun die entsprechende Funktionalität bereitgestellt werden:
• Leiten Sie eine neue generische Klasse CompIterDoublyLinkedList von Ihrer Implementierung der Klasse IterableDoublyLinkedList ab, deren Typ T das generische
Interface Comparable implementiert.
Dieses Interface deklariert die Methode int compareTo(T o), die das aktuelle Objekt
mit dem durch o referenzierten Objekt vergleicht und im Falle von this < o“ eine
”
negative Zahl (z.B. -1), this = o“ die Zahl 0 sowie this > o“ eine positive Zahl
”
”
(z.B. +1) zurückgibt.
• Kopieren Sie die Methoden sort() und quicksort() aus der Vorlage zur Aufgabe 11.1
und passen Sie diese entsprechend Ihrer generischen Liste an, so daß sie sortiert werden
kann. Machen Sie sich dabei zu Nutze, dass die Objekte vom Typ T nun durch das generische Comparable-Interface die Methode int compareTo(T o) zur Verfügung stellen.
Hinweis: Den Algorithmus ansich müssen Sie natürlich nicht verändern.
Lösungsvorschlag 11.4
7
package g e n e r i c ;
public c l a s s CompIterDoublyLinkedList<T extends Comparable<T>> extends
I t e r a b l e D o u b l y L i n k e d L i s t <T> {
/∗ ∗
∗ A u x i l i a r y f u n c t i o n t h a t implements t h e q u i c k s o r t a l g o r i t h m .
∗ @return
a r e f e r e n c e t o a new s o r t e d l i s t
∗/
private L is tElem ent <T> q u i c k s o r t ( ListEleme nt <T> head ) {
i f ( head . next == head ) // Only one element , so s t o p r i g h t h e r e .
return head ;
// S p l i t t h e l i s t i n two l i s t s where one h o l d s a l l e l e m e n t s
smaller
// than t h e p i v o t e l e m e n t w h i l e t h e o t h e r one h o l d s a l l
elements that
// a r e g r e a t e r than or e q u a l t o t h e p i v o t e l e m e n t .
f i n a l L is tElem ent <T> p i v o t = head ;
Lis tElem ent <T> l t L i s t = null ;
Lis tElem ent <T> g e q L i s t = null ;
fo r ( ListEleme nt <T> e l t = head . next ; e l t != head ; e l t = e l t .
next ) {
i f ( e l t . i n f o . compareTo ( p i v o t . i n f o ) < 0 ) {
// L ess than t h e p i v o t e l e m e n t .
i f ( l t L i s t == null ) {
l t L i s t = new L is tElem ent <T>( e l t . i n f o ) ;
} else
l t L i s t = i n s e rt ( e l t . info , l t L i s t ) ;
} else {
// G r e a t e r than or e q u a l t o t h e p i v o t e l e m e n t .
i f ( g e q L i s t == null ) {
g e q L i s t = new L is tElem ent <T>( e l t . i n f o ) ;
} else
geqList = in s e r t ( e l t . info , geqList ) ;
}
}
// R e c u r s i v e l y s o r t b o t h l t L i s t and g e q L i s t .
i f ( l t L i s t != null )
ltList = quicksort ( ltList ) ;
i f ( g e q L i s t != null )
geqList = i n s e r t ( pivot . info , quicksort ( geqList ) ) ;
else
g e q L i s t = new L is tElem ent <T>( p i v o t . i n f o ) ;
// Return t h e r e s u l t .
i f ( l t L i s t == null ) {
return g e q L i s t ;
} else {
// Concatenate b o t h l i s t s and r e t u r n t h e r e s u l t .
f i n a l L is tElem ent <T> l t L i s t L a s t = l t L i s t . prev ;
f i n a l L is tElem ent <T> g e q L i s t L a s t = g e q L i s t . prev ;
l t L i s t L a s t . next = g e q L i s t ;
l t L i s t . prev = g e q L i s t L a s t ;
g e q L i s t L a s t . next = l t L i s t ;
8
g e q L i s t . prev = l t L i s t L a s t ;
return l t L i s t ;
}
}
/∗ ∗
∗ Sorts the l i s t .
∗/
public void s o r t ( ) {
// Empty l i s t s a r e t r i v i a l .
i f ( s e n t i n e l . next == s e n t i n e l )
return ;
// During t h e s o r t i n g p r o c e s s , t h e s e n t i n e l e l e m e n t i s
superfluous .
// Hence d e c o u p l e t h e e l e m e n t s from t h e s e n t i n e l .
Lis tElem ent <T> f i r s t = s e n t i n e l . next ;
Lis tElem ent <T> l a s t = s e n t i n e l . prev ;
f i r s t . prev = l a s t ;
l a s t . next = f i r s t ;
// Then perform t h e q u i c k s o r t a l g o r i t h m .
Lis tElem ent <T> r e s u l t = q u i c k s o r t ( f i r s t ) ;
// F i n a l l y r e i n s t a t e t h e s e n t i n e l e l e m e n t .
s e n t i n e l . next = r e s u l t ;
s e n t i n e l . prev = r e s u l t . prev ;
r e s u l t . prev . next = s e n t i n e l ;
r e s u l t . prev = s e n t i n e l ;
}
public s t a t i c void main ( S t r i n g [ ] a r g s ) {
CompIterDoublyLinkedList<I n t e g e r > l s t = new
CompIterDoublyLinkedList<I n t e g e r >() ;
fo r ( int i = 0 ; i < 5 0 ; i ++)
l s t . append ( ( int ) ( 1 0 0 ∗ Math . random ( ) ) ) ;
System . out . p r i n t l n ( l s t ) ;
l s t . sort () ;
System . out . p r i n t l n ( l s t ) ;
}
}

Documents pareils