Es gibt einen absolut wundervollen kleinen Hack für Ruby, der vielseitig einsetzbar ist:
class Symbol def to_proc proc { |obj, *args| obj.send(self, *args) } end end
Nun kann man faszinierende Dinge tun, indem man
bla { |manamana| manamana.meth }
abkürzt mit bla(&:meth):
# map: 'meine gitarre ist kaputt'.split.map(&:capitalize).join(' ') #-> "Meine Gitarre Ist Kaputt"
# Sortieren nach Größe: puts %w'Yodas Grammatik ist ziemlich schlecht'. sort_by(&:size).reverse.join(' ') #-> Grammatik ziemlich schlecht Yodas ist
# Alle Administratoren finden: @admins = User.find(:all).select(&:admin?)
# fold und Summations-Funktion: module Enumerable def fold msg inject(&msg) end end Array(1..100).fold(:+) #-> 5050 (= 1 + 2 + ... + 100)
Das letzte ähnelt der foldl1-Funktion aus Haskell. Gefunden habe ich das in den Facets und bei Dave Thomas, aber der Hack ist mindestens seit Anfang 2004 auf Ruby-Talk im Umlauf, also vermutlich nicht Daves Erfindung.
Am einfachsten kommt man an die tolle Methode, indem man den Code kopiert oder sich facets oder extensions via Rubygems installiert:
require 'facet/symbol/to_proc' # oder require 'extensions/symbol'
Man muss an die Klammern denken, damit Ruby den Operator richtig erkennt. Es gibt auch noch ein paar Schwierigkeiten, nicht alle Methodenaufrufe lassen sich "symboltoprocen".
Aber man wird demnächst sicher häufiger auf diese Konstruktion stoßen, denn sie ist in Rails 1.1 bereits in ActiveSupport eingebaut.
In Ruby hat man oft mit kurzen Blöcken zu tun, die nicht viel mehr tun, als eine Methode ihres Arguments aufzurufen. Zum Beispiel haben wir eine Datei, in der Hunderte von Zahlen stehen, die wir in einem Array speichern möchten:
numbers = File.read('primes.txt').scan(/d+/) numbers.map! { |number| number.to_i }
Formulieren wir das ganze um: Eine Ruby-Methode aufzurufen bedeutet, eine Botschaft an ein Objekt zu senden; in Ruby werden diese Botschaften durch Symbole dargestellt. Die Methode send(self) tut genau das:
numbers.map! { |number| number.send(:to_i) }
Das langweilige Muster { |x| x.send(:msg) } ist nur noch von dem verwendeten Symbol abhängig. Machen wir also eine Methode der Klasse Symbol daraus:
class Symbol def to_proc proc { |x| x.send(self) } end end
:to_i.to_proc liefert also nun ein Proc-Objekt, das wir mithilfe des &-Operators als Block-Argument an map! übergeben können:
numbers.map!(&:to_i.to_proc)
Sieht hässlicher aus als vorher. Glücklicherweise ruft Ruby selber to_proc auf, wenn man ein &-Argument übergibt:
numbers.map!(&:to_i)
Jetzt erlauben wir nur noch zusätzliche Argumente (mit *args) und fertig ist die Methode!
class Symbol def to_proc proc { |x, *args| x.send(self, *args) } end end
Kommentar schreiben
Kommentare
Juchu! Mein erster Blog-Eintrag. Das Highlighting ist übrigends mit CodeRay realisiert (gem install coderay). Das Script zum Umwandeln findet ihr unter http://code.rubychan.de/highlight-wordpress.rb.