Ruby
Eine eigene Implementierung der Ruby-Methode Array.include?
Hintergrund: Das Pattern Map
Das Programmierpattern
Map
wendet eine benutzerdefinierte Funktion auf die Werte einer Liste an und liefert eine neue Liste mit den Funktionswerten.
Hintergrund: Das Pattern Fold
Das Programmierpattern
Fold
führt eine benutzerdefinierte Berechnung (oder auch Akkumulation) über alle Werte in einer Liste durch - es "faltet" solche Werte zu einem zusammen.
Die Implementierung
Im Folgenden wurde die Methode
Array.include?
mithilfe der Porgrammierpatterns
map
und
fold
implementiert und
exists?
genannt.
Array.include?
liefert einen boolschen Wert und gibt Auskunft darüber, ob ein Eintrag schon im Array enthalten ist (basierend auf dem Vergleich
Object.==
)
1 class MyArray < Array
2
3 def exists?(anEntry)
4 map{ |e| e == anEntry }.fold(false){ |a, b| a || b }
5 end
6
7 def fold(acc)
8 map { |e| acc = yield(acc, e) }
9 acc
10 end
11
12 def map(&block)
13 MyArray.new(super.map { |e| block.call(e) })
14 end
15
16 end
Da die Arrayklasse schon eine Map-Methode besitzt, müssen wir nur noch
fold
implementieren. Dies erfordert dennoch eine kleine Erweiterung der Map-Methode:
12 def map(&block)
13 MyArray.new(super.map { |e| block.call(e) })
14 end
Der Rückgabewert von
Array.map
ist ein Array vom Typ
Array
. Da wir aber in Zeile 4
MyArray.fold
auf diesen Rückgabewert anwenden wollen, müssen wir
Array.map
mit
MyArray.map
überschreiben und eine Konvertierung des Rückgabewerts vornehmen. In Zeile 12 übernehmen wir den an
MyArray.map
übergebenen Block in der Variablen
block
vom Typ
Proc
. In Zeile 15, die auch den Rückgabewert der Methode festlegt, erzeugen wir nun ein Array vom Typ
MyArray
und füllen dieses mit dem Ergebnis der schon vorhandenen Methode
Array.map
.
block.call(e)
führt den an
block
übergebenen Block mit dem Argument
e
aus.
7 def fold(acc)
8 map { |e| acc = yield(acc, e) }
9 acc
10 end
Das Argument
acc
dient als Akkumulatorvariable. Sie kann beim Aufruf von
MyArray.fold
durch den Benutzer initialisiert werden. Der an
MyArray.fold
übergebene Block enthält die Akkumulationsvorschrift. Diese wird durch die Kombination von
yield
, welches den übergebenen Block ausführt, und
MyArray.map
auf allen Werten der Liste angewendet. In Zeile 9 wird der akkumulierte Wert als Rückgabewert der Methode definiert.
Bewertung
Ich persönlich bevorzuge eine solche Lösung des Suchen-Problems, da eine allgemeine Implementierung der Programmierpatterns
map
und
fold
sehr mächtig ist und mit Sicherheit an weiteren Stellen im Programm verwendet werden kann. Die eher klassische Implementierung etwa per for-Schleife über die Listenelemente ist auf den ersten Blick vielleicht lesbarer, aber auf jeden Fall speziell und nur begrenzt in anderen Fällen wiederzuverwenden. Die Patterns ermöglichen eine Abstraktion bis auf die wesentlichen Teile des Suchen-Problems - nämlich den Vergleich und die Informationsfilterung.
--
DennisKlein - 28 Sep 2007