Generizität: Difference between revisions
No edit summary |
No edit summary |
||
Line 4: | Line 4: | ||
Algortihmen und Datenstrukturen so zu entwerfen und implementieren, dass sie möglichst<br/> vielvältig verwendbar sind. | |||
=== Gemeint sind : === | |||
*verschiedene Anwendungen | *verschiedene Anwendungen | ||
Line 17: | Line 17: | ||
Beispiel : Kopieren eines Containers | '''Beispiel : ''' Kopieren eines Containers | ||
==== 1.Variante: ==== | |||
def copyArray(a): | def copyArray(a): | ||
Line 29: | Line 29: | ||
==== 2.Variante: ==== | |||
class Node : | class Node : | ||
Line 36: | Line 37: | ||
return k | return k | ||
==== 3.Variante: ==== | |||
def copyArrayToList(a) : | def copyArrayToList(a) : | ||
if len(a) == 0 : return None | if len(a) == 0 : return None | ||
first = last = Node (a[0], None) | |||
for k in a[1:] : | for k in a[1:] : | ||
last.next = Node(k, None) | |||
last = last.next | |||
return first | return first | ||
==== 4.Variante: ==== | |||
def copyListToArray(l): | def copyListToArray(l): | ||
r = [] | r = [] | ||
Line 56: | Line 57: | ||
'''Beobachtung''' : | ==== '''Beobachtung''' : ==== | ||
Für N Datenstrukuren ist der Implementaionsaufwand <math>O({N^2})</math>. | Für '''N Datenstrukuren''' ist der Implementaionsaufwand <math>O({N^2})</math>. | ||
Alle Funktionen machen das gleiche mit uninteressanten Unterschied | Alle Funktionen machen das gleiche mit uninteressanten Unterschied | ||
Line 64: | Line 65: | ||
'''Verbesserung durch Verallgemeinerung zweier Aspekte''' : | '''Verbesserung durch Verallgemeinerung zweier Aspekte''' : | ||
*Navigation durch die Quelldaten | |||
*Aufbauen der Zieldatenstruktur | |||
Wie kann man so zwischen einem Array und einer Liste navigieren, so dass man den Unterschied nicht mehr merkt? | |||
--> Vereinheitlichung der Zieldatenstruktur : | -->''' Vereinheitlichung der Zieldatenstruktur :''' | ||
*standardisierte Funktion "append" | |||
*Array hat sie schon | |||
*Liste : definiere Klasse DoublyLinkedList | |||
Line 98: | Line 98: | ||
self.size = 0 | self.size = 0 | ||
def__len__(self): return self.size #len(l) | |||
def append(self, value): | |||
DoublyLinkedNode(value, self.sentinel) | DoublyLinkedNode(value, self.sentinel) | ||
def__iter__(self): | def__iter__(self): | ||
Line 105: | Line 105: | ||
2. Navigation in der Quelldatenstruktur(Iteration) soll für alle Datenstrukturen funktionieren | === 2. Navigation in der Quelldatenstruktur(<u>Iteration</u>) soll<br/> für alle Datenstrukturen funktionieren === | ||
*Objekt, dass auf ein Element des Containers zeigt | |||
*zum nächsten Element weiter rücken kann | |||
*anzeigt, wenn das Ende der Sequenz erreicht ist | |||
Line 118: | Line 118: | ||
liste = genericCopy(array, DoublyLinkedList()) | liste = genericCopy(array, DoublyLinkedList()) | ||
Statt copyArrayToList | <u> Statt copyArrayToList </u> | ||
array2 = genericCopy(array,[]) | array2 = genericCopy(array,[]) | ||
Statt copyArray | <u> Statt copyArray </u> | ||
array3 = genericCopy(liste,[]) | array3 = genericCopy(liste,[]) | ||
Statt copyListToArray | <u> Statt copyListToArray </u> | ||
iter = quelle__iter__() | '''was tun''' " for k in quelle": | ||
iter = quelle__iter__() | |||
try : | |||
while True : | while True : | ||
k = iter.next() | k = iter.next() | ||
Line 151: | Line 149: | ||
return ListIterator(self.node) # Pythonkonvention, Kopie des Iterators erzeugen | return ListIterator(self.node) # Pythonkonvention, Kopie des Iterators erzeugen | ||
besser stattdessen : | '''besser stattdessen''' : | ||
self__class__(self.node) | self__class__(self.node) | ||
Line 168: | Line 166: | ||
Verallgemeinerung auf Funktionen die " etwas tun": | '''Verallgemeinerung auf Funktionen die " etwas tun":''' | ||
<code>def sumArray(a): | <code>def sumArray(a): | ||
Line 183: | Line 181: | ||
l =l.next | l =l.next | ||
===3.Funktoren=== | |||
Zur Verallgemeinerung werden Funktoren eingerichtet: | '''Zur Verallgemeinerung werden Funktoren eingerichtet:''' | ||
*Funktor muss "callable" sein : | |||
falls f Funktor: v = f(a1, a2) | |||
'''callable''' ist z.B. eine Funktion | |||
*Objekt wo __call___ definiert | |||
def doSomethingGeneric(functor, iterator, for k in iterator: intial): | def doSomethingGeneric(functor, iterator, for k in iterator: intial): | ||
initial = functor(initial, k) | |||
return | return initial | ||
m = doSomethingGeneric(max,list -1111111) | |||
statt maxList | |||
def add(x,y) return x+y | |||
s = doSomethingGeneric(add, array,o) | |||
statt sumArray | |||
def append(x,y): | |||
x.append(y) | |||
return x | |||
array4 = doSomethingGeneric(append, array[]) | |||
<u>Statt genericCopy</u> | |||
*'''doSomethingGeneric'''() gibt es in vielen Programmiersprachen | |||
**in Python : reduce in C++ : accumulate | |||
'''verwandte generische Funkionen''' | |||
map: [x1, x2,...] --> [f(x1),f(x2),...] alle funktionalen Sprachen ( Haskell,Lisp,...) | |||
map ist in C++ "transform" | |||
===Offered Interface versus Required Interface=== | |||
'''Interface:''' | |||
*standardisierte Schnittstelle zwischen Algorithmen und Datenstruktur | |||
====Offered Interface:==== | |||
*Funktionalität, die eine Datenstruktur anbietet. | |||
*Die Datensruktur sollte möglichst vielseitig sein. | |||
'''z.B. PythonList:''' | |||
* dynamisches Array | |||
* stack | |||
* deque | |||
* linkedList | |||
*standardisiert durch abstrakte Datentypen | |||
====Required Interface:==== | |||
*Funktionalität, die Algorihmus benutzt | |||
*das '''required Interface''' ist meist weniger als '''das offered Interface''' | |||
z.B.: | |||
'''RI''' lesender Zugriff | |||
'''OI''' lesender Zugriff schreibender Zugriff Konstruktor remove... | |||
* standardisiert über Konzepte | |||
* ADT sind Sammlungen zusammengehörender Konzepte | |||
* RI sollten <u>minimal</u> sein | |||
====Konzepte ( + Hierarchie)==== | |||
*copy Constructible ( P: copy.deepcopy) | |||
*Default Constructible | |||
v1 = v.__class__() # DoublylinkedNode | |||
*EqualityComparable('=='), LessThanComparable('<') | |||
ThreeWayComparable(__cmp__) | |||
* Indexable("a[k]") | |||
* Mapping("a[key]") | |||
* Iteratoren : Forward(next), BidirektionalIterator(next, prev), RandomAccessIterator(nex(k)) | |||
* Container : Sequence | |||
====Mathematische Konzepte :==== | |||
Addable(__add__) | |||
Subtractable(__sub__) | |||
Multiplyable(__mul__) | |||
Dividable(__div__) | |||
'''Ein offered Interface is mehr als ein required Interface.''' |
Revision as of 17:04, 14 June 2008
Definition in der Informatik :
Algortihmen und Datenstrukturen so zu entwerfen und implementieren, dass sie möglichst
vielvältig verwendbar sind.
Gemeint sind :
- verschiedene Anwendungen
- mit vielen Kombinationsmöglichkeiten
- als wiederverwendbare Bibliothek
--> ohne Neuimplemenation
- Code austauschen in Bibliotheken
Beispiel : Kopieren eines Containers
1.Variante:
def copyArray(a): r =[] for k in a r.append(k) return k
2.Variante:
class Node : def__init__(self,data,next) self.data = data self.next = next return k
3.Variante:
def copyArrayToList(a) : if len(a) == 0 : return None first = last = Node (a[0], None) for k in a[1:] : last.next = Node(k, None) last = last.next return first
4.Variante:
def copyListToArray(l): r = [] while l is not in None : r.append(l.data) l = l.next return r
Beobachtung :
Für N Datenstrukuren ist der Implementaionsaufwand <math>O({N^2})</math>. Alle Funktionen machen das gleiche mit uninteressanten Unterschied
Verbesserung durch Verallgemeinerung zweier Aspekte :
- Navigation durch die Quelldaten
- Aufbauen der Zieldatenstruktur
Wie kann man so zwischen einem Array und einer Liste navigieren, so dass man den Unterschied nicht mehr merkt?
--> Vereinheitlichung der Zieldatenstruktur :
- standardisierte Funktion "append"
- Array hat sie schon
- Liste : definiere Klasse DoublyLinkedList
class SentinelTag : pass # keine Daten class DoublyLinkedNode: def__init__(self,data = sentinelTag(), next = None) if next is None: self.prev = self.next = self else : self.next = next self.prev = next.prev next.prev.next = self next.prev = self
def isSentinel(self) : return isinstance(self.data, sentinelTag)
class DoublyLinkedList : def__init__(self): self.sentinel = DoublyLinkedNode() self.size = 0
def__len__(self): return self.size #len(l) def append(self, value): DoublyLinkedNode(value, self.sentinel) def__iter__(self): return ListIterator(self.sentinel.next)
- Objekt, dass auf ein Element des Containers zeigt
- zum nächsten Element weiter rücken kann
- anzeigt, wenn das Ende der Sequenz erreicht ist
def genericCopy(quelle, ziele) : for k in quelle : ziel.append(k) return ziel
liste = genericCopy(array, DoublyLinkedList()) Statt copyArrayToList
array2 = genericCopy(array,[]) Statt copyArray
array3 = genericCopy(liste,[]) Statt copyListToArray
was tun " for k in quelle": iter = quelle__iter__() try : while True : k = iter.next() ...# Schleifeninhalt except StopIteration: pass
class ListIterator: def__init__(self, node): self.node = node def next(self): if self.node.isSentinel(): raise StopIteraion() #Python Konvention v = self.node.data self.node = self.node.next # zeigt Ende der Sequenz return v # Pythonkonvention, gebe vorigen Wert zurück
def__iter__(self): return ListIterator(self.node) # Pythonkonvention, Kopie des Iterators erzeugen
besser stattdessen :
self__class__(self.node)
class ReverseListIterator(ListIterator) def next(self): if self.node isSentinel(): raise StopIteration() r = self.node.data self.node = self.node.prev return r
revarray = genericCopy(list, reverseIter()[]), revlist = genericCopy(reversed(array), DoublyLinkedList())
Verallgemeinerung auf Funktionen die " etwas tun":
def sumArray(a):
s = 0
for k in a :
s+=a # s = add(s,k)
return s
def maxList(l): m = -1111111 while l is not None [not l is Sentinel(l)] m = max(m, l.data) # max ist eingebaute Funkion in Python l =l.next
3.Funktoren
Zur Verallgemeinerung werden Funktoren eingerichtet:
- Funktor muss "callable" sein :
falls f Funktor: v = f(a1, a2) callable ist z.B. eine Funktion
- Objekt wo __call___ definiert
def doSomethingGeneric(functor, iterator, for k in iterator: intial): initial = functor(initial, k) return initial
m = doSomethingGeneric(max,list -1111111) statt maxList
def add(x,y) return x+y s = doSomethingGeneric(add, array,o) statt sumArray
def append(x,y): x.append(y) return x
array4 = doSomethingGeneric(append, array[]) Statt genericCopy
- doSomethingGeneric() gibt es in vielen Programmiersprachen
- in Python : reduce in C++ : accumulate
verwandte generische Funkionen
map: [x1, x2,...] --> [f(x1),f(x2),...] alle funktionalen Sprachen ( Haskell,Lisp,...)
map ist in C++ "transform"
Offered Interface versus Required Interface
Interface:
- standardisierte Schnittstelle zwischen Algorithmen und Datenstruktur
Offered Interface:
- Funktionalität, die eine Datenstruktur anbietet.
- Die Datensruktur sollte möglichst vielseitig sein.
z.B. PythonList:
- dynamisches Array
- stack
- deque
- linkedList
- standardisiert durch abstrakte Datentypen
Required Interface:
- Funktionalität, die Algorihmus benutzt
- das required Interface ist meist weniger als das offered Interface
z.B.:
RI lesender Zugriff OI lesender Zugriff schreibender Zugriff Konstruktor remove...
- standardisiert über Konzepte
- ADT sind Sammlungen zusammengehörender Konzepte
- RI sollten minimal sein
Konzepte ( + Hierarchie)
- copy Constructible ( P: copy.deepcopy)
- Default Constructible
v1 = v.__class__() # DoublylinkedNode
- EqualityComparable('=='), LessThanComparable('<')
ThreeWayComparable(__cmp__)
- Indexable("a[k]")
- Mapping("a[key]")
- Iteratoren : Forward(next), BidirektionalIterator(next, prev), RandomAccessIterator(nex(k))
- Container : Sequence
Mathematische Konzepte :
Addable(__add__) Subtractable(__sub__) Multiplyable(__mul__) Dividable(__div__)
Ein offered Interface is mehr als ein required Interface.