You are here: GSI Wiki>Linux Web>Ruby (2007-09-28, DennisKlein)EditAttach

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
Topic revision: r3 - 2007-09-28, DennisKlein