Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Einführung
2 Mathematische und technische Grundlagen
3 Hardware
4 Netzwerkgrundlagen
5 Betriebssystemgrundlagen
6 Windows
7 Linux
8 Mac OS X
9 Grundlagen der Programmierung
10 Konzepte der Programmierung
11 Software-Engineering
12 Datenbanken
13 Server für Webanwendungen
14 Weitere Internet-Serverdienste
15 XML
16 Weitere Datei- und Datenformate
17 Webseitenerstellung mit (X)HTML und CSS
18 Webserveranwendungen
19 JavaScript und Ajax
20 Computer- und Netzwerksicherheit
A Glossar
B Zweisprachige Wortliste
C Kommentiertes Literatur- und Linkverzeichnis
Stichwort

Jetzt Buch bestellen
Ihre Meinung?

Spacer
IT-Handbuch für Fachinformatiker von Sascha Kersken
Der Ausbildungsbegleiter
Buch: IT-Handbuch für Fachinformatiker

IT-Handbuch für Fachinformatiker
Rheinwerk Computing
1216 S., 6., aktualisierte und erweiterte Auflage, geb.
34,90 Euro, ISBN 978-3-8362-2234-1
Pfeil 9 Grundlagen der Programmierung
Pfeil 9.1 Die Programmiersprache C
Pfeil 9.1.1 Das erste Beispiel
Pfeil 9.1.2 Elemente der Sprache C
Pfeil 9.1.3 Die C-Standardbibliothek
Pfeil 9.2 Java
Pfeil 9.2.1 Grundlegende Elemente der Sprache Java
Pfeil 9.2.2 Objektorientierte Programmierung mit Java
Pfeil 9.2.3 Dateizugriffe in Java
Pfeil 9.3 Perl
Pfeil 9.3.1 Das erste Beispiel
Pfeil 9.3.2 Elemente der Sprache Perl
Pfeil 9.4 Ruby
Pfeil 9.4.1 Das erste Beispiel
Pfeil 9.4.2 Ruby-Grundelemente
Pfeil 9.4.3 Objektorientierung in Ruby
Pfeil 9.5 Zusammenfassung

Rheinwerk Computing - Zum Seitenanfang

9.4 RubyZur nächsten Überschrift

Nun wird es Zeit für die letzte Programmiersprache in diesem Kapitel: Ruby ist wie Perl eine Skriptsprache, aber eine objektorientierte, und zwar in absolut konsequentem Maße – selbst Java macht mehr Kompromisse. Der Name bedeutet Rubin und spielt natürlich auf Perl (die »Perle«) an. Wenn Ihnen die Ruby-Einführung in diesem Abschnitt zusagt, können Sie sich unter http://www.oreilly.de/german/freebooks/rubybasger/ kostenlos mein vergriffenes Buch »Praxiswissen Ruby« (Köln 2007, O’Reilly Verlag) herunterladen; darin finden Sie noch viel mehr interessante Beispiele und Erläuterungen.

Ruby wurde seit 1993 von dem japanischen Programmierer Yukihiro »Matz« Matsumoto entwickelt, der mit den verfügbaren Sprachen Perl und Python unzufrieden war. Jahrelang verbreitete sich die Sprache nur innerhalb Japans, bis im Jahr 2000 das erste englischsprachige Buch erschien (»Programming Ruby« von Andy Hunt und Dave Thomas, Pragmatic Programmers. Diese erste Auflage ist unter http://www.rubycentral.com/book/ frei im Netz verfügbar). Besonders bekannt wurde Ruby durch das Web-Framework Ruby on Rails, das der Däne David Heinemeier Hansson im Jahr 2004 auf der Basis von Ruby entwickelte (siehe Kapitel 18, »Webserveranwendungen«).

Die aktuelle Ruby-Version ist 1.9, und 2.0 steht kurz vor der Fertigstellung. Im Vergleich zur Vorgängerversion 1.8 bringt Version 1.9 vor allem eine Performancesteigerung durch den Einsatz einer virtuellen Maschine mit sich; zudem wurden einige zusätzliche syntaktische Freiheiten eingeräumt.

Wenn Ruby nicht Teil Ihrer Linux- oder Unix-Distribution ist, können Sie das Paket mit dem Interpreter und anderen nützlichen Tools unter http://www.ruby-lang.org/ herunterladen. Für Windows finden Sie dort sogar einen komfortablen Installer, den Sie einfach per Doppelklick ausführen können. Die jeweilige Installationsanleitung finden Sie ebenfalls dort.

Ein interessantes Feature, das zum Lieferumfang der Programmiersprache gehört, nennt sich Interactive Ruby. Geben Sie dazu in der Eingabeaufforderung oder in einem Terminalfenster irb ein (das Tool befindet sich im bin-Verzeichnis Ihrer Ruby-Version, das sich nach korrekter Installation automatisch in Ihrem PATH befindet). Daraufhin meldet sich die interaktive Ruby-Shell mit einem Prompt wie diesem:

irb(main):001:0>

Wenn Sie nicht möchten, dass die Shell Ihre Eingabezeilen zählt und andere Informationen ausgibt, können Sie sie auch mithilfe von irb --simple-prompt starten – daraufhin wird nur noch >> ausgegeben.

Wenn Sie nun einen Ausdruck ausgeben, wird er sofort ausgewertet; sein Wert wird hinter => ausgegeben. Auf diese Weise können Sie irb im einfachsten Fall als praktischen Taschenrechner (mit beliebig vielen Variablen als Zwischenspeicher) benutzen. Probieren Sie es einfach aus. Hier einige Beispiele:

>> 7 * 6
=> 42
>> 42 / 2 + 2
=> 23
>> 39 / 2
=> 19

Das letzte Ergebnis scheint falsch zu sein. Die Erklärung: Da Ruby konsequent objektorientiert ist, gehört jedes Literal einer bestimmten Klasse an. Geben Sie Folgendes ein, um zu ermitteln, zu welcher Klasse 39 und das Rechenergebnis gehören:

>> 39.class
=> Fixnum
>> (39 / 2).class
=> Fixnum

Die Ausgabe lautet jeweils Fixnum; dies ist die Ruby-Klasse für Integer-Zahlen bis zur Wortbreite des Prozessors, also meist 32 oder 64 Bit. Daneben gibt es noch Bignum für beliebig große Ganzzahlen. Um das gewünschte Fließkommaergebnis zu erhalten, muss mindestens einer der Operanden als Fließkommazahl geschrieben werden; dies erreichen Sie durch Anhängen von .0:

>> 39.0 / 2
=> 19.5
>> 39.0.class
=> Float

Beenden Sie irb nun erst einmal, indem Sie exit eingeben.


Rheinwerk Computing - Zum Seitenanfang

9.4.1 Das erste BeispielZur nächsten ÜberschriftZur vorigen Überschrift

Das kurze Beispielprogramm, das bereits in den drei anderen Programmiersprachen vorgestellt wurde, lautet in Ruby folgendermaßen:

puts "Hallo Welt!"
print "Ihr Name, bitte: "
name = gets.chomp
puts "Hallo #{name}!"

Speichern Sie es als hallo.rb, und geben Sie auf der Konsole innerhalb seines Verzeichnisses Folgendes ein, um es mithilfe des Ruby-Interpreters auszuführen:

> ruby hallo.rb

Unter Unix können Sie das Skript auf Wunsch auch mithilfe von chmod ausführbar setzen und eine Shebang-Zeile wie diese hinzufügen:

#!/usr/bin/ruby -w

Dadurch kann das Skript wie gehabt ohne expliziten Aufruf des Interpreters ausgeführt werden. Dies ist bei Ruby allerdings nicht so allgemein üblich wie in Shell- und Perl-Skripten. Unter Windows können Sie den Interpreter übrigens ohne weitere Schritte weglassen, da der Ruby-Installer die Dateiendung .rb automatisch zur Umgebungsvariablen PATHEXT für ausführbare Dateitypen hinzufügt.

Hier die Beschreibung der einzelnen Programmzeilen:

  • puts "Hallo Welt!"

    Gibt den Text »Hallo Welt!« mit anschließendem Zeilenumbruch aus. puts ist eigentlich eine Methode der eingebauten Klasse IO, die Ein- und Ausgabekanäle kapselt; als solche wird sie in der Form IO-Objekt.puts aufgerufen. Allerdings ist die Kurzfassung ohne Objektbezug ebenfalls erlaubt. In diesem Fall wird der zuletzt gewählte Ausgabekanal verwendet, in der Regel also (wie hier) die Standardausgabe.

  • print "Ihr Name, bitte: "

    Gibt den Text »Ihr Name, bitte: « aus. Auch print ist eine I/O-Methode, die hier standardmäßig auf STDOUT schreibt. Im Unterschied zu puts gibt diese Methode keinen automatischen Zeilenumbruch aus.

  • name = gets.chomp

    In der Variablen name wird das Ergebnis von gets.chomp gespeichert. Zunächst liest die Methode gets eine Zeile aus einem Eingabekanal (hier STDIN). Das Ergebnis wird durch chomp verarbeitet; diese Methode bewirkt dasselbe wie die gleichnamige Perl-Funktion: Wenn das letzte Zeichen Whitespace ist (Leerzeichen, Tabulator oder Zeilenumbruch), wird es entfernt. Beachten Sie die objektorientierte Schreibweise: Es heißt in Ruby stets String-Objekt.chomp und nicht etwa chomp(String).

  • puts "Hallo #{name}!"

    Hier wird »Hallo« ausgegeben, gefolgt von einem Leerzeichen, dem eingegebenen Namen und einem Ausrufezeichen. Das Konstrukt #{...} ermöglicht die Auswertung beliebig komplexer Ausdrücke innerhalb von Strings; diese müssen dazu in doppelten Anführungszeichen stehen. Probieren Sie es in irb aus. Beispiel:

    >> "3 + 7 = #{3 + 7}"
    => "3 + 7 = 10"
    >> # Gegenprobe: einfache Anführungszeichen
    ?> '3 + 7 = #{3 + 7}'
    => "3 + 7 = \#{3 + 7}"

    Wie Sie sehen, werden die String-Ergebnisse jeweils in doppelten Anführungszeichen angezeigt. Im zweiten Beispiel wird die Raute einfach durch einen Backslash zur Escape-Sequenz, damit ihre ausdrucksbildende Wirkung unterbleibt. Was hier auch bereits gezeigt wird: Kommentare werden wie in Perl durch eine Raute eingeleitet und reichen bis zum Zeilenende.


Rheinwerk Computing - Zum Seitenanfang

9.4.2 Ruby-GrundelementeZur nächsten ÜberschriftZur vorigen Überschrift

Ruby weist gegenüber den bisher betrachteten Sprachen einige interessante Besonderheiten auf. In diesem Abschnitt werden die Basiselemente der Sprache vorgestellt; auf die Programmierung von Klassen und andere Aspekte der Objektorientierung wird anschließend in Abschnitt 9.4.3, »Objektorientierung in Ruby«, eingegangen.

Syntaktische Besonderheiten

Wie Sie bereits an dem kleinen Beispielprogramm gesehen haben, werden Anweisungen in Ruby normalerweise durch einen Zeilenumbruch und nicht durch ein Semikolon getrennt. Das Semikolon kann allerdings auf Wunsch benutzt werden, um mehrere Anweisungen in derselben Zeile unterzubringen. Wenn Sie umgekehrt eine Anweisung auf mehrere Zeilen aufteilen möchten, können Sie entweder den aus C und anderen Programmiersprachen bekannten Backslash (\) am jeweiligen Zeilenende verwenden, oder aber Sie trennen die Anweisungen an einer Stelle, an der logisch betrachtet noch etwas folgen muss. Betrachten Sie als Demonstration für Letzteres die folgenden irb-Beispiele:

>> 2 +
?> 2
=> 4

>> 2
=> 2
>> + 2
=> 2

Im ersten Fall steht das Pluszeichen am Zeilenende, und der Interpreter erwartet einen weiteren Operanden. Im zweiten Beispiel dagegen ist der Ausdruck 2 in der ersten Zeile komplett, und + 2 gilt als neuer, unabhängiger Ausdruck. In anderen Fällen – etwa bei 2 * 2 – ist der zweite Teil dann nicht einmal ein gültiger Ausdruck, sondern ergibt eine Fehlermeldung.

Ein weiterer Sonderfall in Ruby ist, dass die Klammern mit den Argumenten eines Methodenaufrufs ohne Abstand zum Methodennamen geschrieben werden müssen.

Literale, Variablen und Ausdrücke

Die in Ruby verfügbaren Literale entsprechen weitgehend denjenigen in den bereits vorgestellten Programmiersprachen. Eine wichtige Besonderheit ist allerdings, dass jedes Literal automatisch einer eingebauten Klasse angehört. Dies können Sie testen, indem Sie in irb jeweils Literal.class eingeben. Für Fixnum und Float haben Sie dies bereits gesehen. Hier einige weitere Beispiele; drei für einzelne und zwei für zusammengesetzte Literale:

>> "hallo".class
=> String
>> (10**100).class
=> Bignum
>> true.class
=> TrueClass
>> (1..7).class
=> Range
>> [1, 2, 3].class
=> Array

Das erste Beispiel, den String, kennen Sie bereits aus den anderen Programmiersprachen. Das zweite verwendet den Operator **, der in Ruby zur Potenzierung dient. Die Zahl 10100 (sie trägt auch den Namen Googol, wonach die Suchmaschine Google benannt wurde) gehört zur Klasse Bignum, den beliebig großen Ganzzahlen. Die Literale true und false bilden jeweils ihre eigenen Klassen, TrueClass beziehungsweise FalseClass. Ein Range (Bereich) kann unter anderem verwendet werden, um zu überprüfen, ob ein Element darin enthalten ist, oder auch, um im Rahmen der später in diesem Kapitel vorgestellten Iteratoren jedes Element nacheinander zu verarbeiten. Arrays schließlich sind in Ruby ähnlich flexibel wie in Perl.

Eine spezielle Sorte von Literalen, die die meisten Programmiersprachen so nicht kennen, sind die Symbole. Jede durch einen Doppelpunkt eingeleitete Sequenz aus Buchstaben, Ziffern und Unterstrichen (das erste Zeichen darf keine Ziffer sein) gilt als eindeutiges Element der Klasse Symbol. Es handelt sich um garantiert unterschiedliche Elemente ohne speziellen Wert, die unter anderem gern als Hash-Schlüssel verwendet werden.

Bei den Zahlen gibt es neben den bereits für andere Programmiersprachen vorgestellten Oktalzahlen mit führender 0 und Hexadezimalzahlen mit dem Präfix 0x auch die Möglichkeit, Dualzahlen darzustellen – sie werden durch ein vorangestelltes 0b gekennzeichnet.

Eine spezielle Art eines numerischen Literals bildet ein Fragezeichen, gefolgt von einem beliebigen anderen Zeichen. Sein Wert ist der ASCII-Code des entsprechenden Zeichens, wobei auch Escape-Sequenzen wie \n oder \t möglich sind. Probieren Sie auch dies in irb aus. Beispiele:

>> ?a
=> 97
>> ?1
=> 49
>> ?\t
=> 9

Für Strings stehen in Ruby (ähnlich wie in Perl) spezielle Quoting-Helfer bereit: %q(...) ersetzt einfache Anführungszeichen, in denen weder Escape-Sequenzen noch eingebettete Ausdrücke ausgewertet werden, während %Q(...) als Ersatz für doppelte Anführungszeichen dient. Statt runder Klammern können Sie auch alle anderen Arten von Klammern oder aber zwei gleiche Zeichen wie !...!, |...| oder @...@ verwenden.

Der Quoting-Operator %w(...) erzeugt ein Array, wobei die Elemente durch Leerzeichen getrennt werden. Hier zwei irb-Beispiele:

>> %w(Mo Di Mi Do Fr Sa So)
=> ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]
>> %w(Hallo\ Welt Hallo\ Leute)
=> ["Hallo Welt", "Hallo Leute"]

Wie das zweite Beispiel zeigt, können Sie Strings mit Leerzeichen als einzelne Elemente verwenden, indem Sie die betreffenden Blanks durch einen Backslash schützen. Der entsprechende Perl-Quoting-Operator qw{...} besitzt diese Fähigkeit übrigens nicht.

Anders als in C und Perl gelten leere Strings und der numerische Wert 0 in Ruby nicht als false, sondern als true. Falsche logische Aussagen ergeben sich nur durch nicht zutreffende Vergleichsoperationen oder deren logische Verknüpfungen; abgesehen davon gilt die leere Objektreferenz nil als falsch.

Variablen werden in Ruby durch einfache Wertzuweisung erzeugt. Die Bezeichner lokaler Variablen besitzen kein besonderes Präfix, sondern bestehen wie üblich aus Buchstaben, Ziffern und Unterstrichen. Sie müssen mit einem Kleinbuchstaben beginnen, da sie sonst als Konstanten betrachtet werden. Wie in den meisten Skriptsprachen sind Variablen auch in Ruby untypisiert, ihr aktueller Wert gehört jedoch jeweils einer bestimmten Klasse an, wie zuvor bereits für Literale gezeigt wurde. Dieser Umgang mit Typen wird als Duck Typing bezeichnet (»if it walks like a duck and quacks like a duck, then it must be a duck«, also »Wenn es watschelt wie eine Ente und quakt wie eine Ente, dann muss es eine sein.«).

Das folgende irb-Beispiel erzeugt eine Variable namens a und weist ihr den Wert 23 zu; anschließend wird die Klassenzugehörigkeit der Variablen (oder vielmehr ihres Werts) abgefragt:

>> a = 23
=> 23
>> a.class
=> Fixnum

Globale Variablen werden in Ruby durch ein vorangestelltes Dollarzeichen gekennzeichnet. Wenn Sie an irgendeiner Stelle in Ihrer Datei eine Definition wie etwa

$planet = "Erde"

vornehmen, gilt die Variable $planet sowohl im (ungekennzeichneten) »Hauptprogramm« als auch in jeder Methode. Sobald Sie voll objektorientiert programmieren – das heißt eine aufeinander aufbauende Klassenhierarchie bilden und mit Instanzen dieser Klassen arbeiten –, sollten Sie allerdings in aller Regel keine globalen Variablen mehr verwenden, sondern Klassen- beziehungsweise Instanzvariablen. Auch für diese gibt es spezielle Präfixe (siehe den Abschnitt »Klassen implementieren und modifizieren« gegen Ende des Kapitels).

Eine Variable, der noch kein Wert zugewiesen wurde, gilt als undefiniert; ein Zugriffsversuch liefert die folgende Fehlermeldung:

>> undefinierte_variable
NameError: undefined local variable or method
`undefinierte_variable' for main:Object

Um dieses Problem zu vermeiden, können Sie die globale Methode defined? zur Überprüfung verwenden (Methodennamen mit angehängtem Fragezeichen sind in Ruby erlaubt und für Testmethoden dieser Art sogar üblich):

>> a = 2
>> defined?(undefinierte_variable)
=> nil
>> defined?(a)
=> "local-variable"

Wie Sie sehen, ist das Ergebnis für eine undefinierte Variable nil und für eine definierte ein String, der ihren Typ angibt. Damit lässt sich das Ergebnis beispielsweise für eine if-Überprüfung verwenden.

Da eine Variable, wie bereits gezeigt, beliebige Objekte speichern kann, sind Arrays und Hashes in Ruby keine speziellen Variablentypen. Ein Array-Literal wird durch eckige Klammern gekennzeichnet. Beispiel:

vokale = ['a', 'e', 'i', 'o', 'u']

Es ist nicht möglich, ein Array implizit durch Indizierung einer undefinierten Variablen zu erzeugen. Sie müssen vorher mindestens ein leeres Array definieren und können es danach füllen:

>> wtage = []
=> []
>> wtage.push('Mo', 'Di', 'Mi')
=> ["Mo", "Di", "Mi"]
>> wtage << 'Do'
=> ["Mo", "Di", "Mi", "Do"]
>> wtage << 'Fr' << 'Sa' << 'So'
=> ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]

Wie Sie sehen, können sowohl die Methode push als auch der Operator << verwendet werden, um weitere Werte hinzuzufügen.

Anders als Perl erlaubt Ruby auch beliebig tief verschachtelte Arrays. Angenommen, die vier Arrays fruehling, sommer, herbst und winter enthalten die Namen der Monate aus der jeweiligen Jahreszeit. Dann lassen sie sich wie folgt zu einem Array von Arrays zusammenfassen:

>> jahreszeiten = [fruehling, sommer, herbst, winter]
=> [["Maerz", "April", "Mai"], ["Juni", "Juli",
"August"], ["September", "Oktober", "November"],
["Dezember", "Januar", "Februar"]]

Der Zugriff auf die einzelnen Elemente erfolgt dann beispielsweise so:

>> jahreszeiten[0][2]
=> "Mai"

Ein Hash steht in geschweiften Klammern, und genau wie in Perl wird der Operator => verwendet, um den jeweiligen Schlüssel vom zugehörigen Wert zu trennen. Es ist jedoch nicht erlaubt, die Schlüssel – sofern sie Strings sind – ohne Anführungszeichen zu schreiben; dafür werden oft die zuvor erwähnten Symbole als Schlüssel verwendet. Hier zwei Beispiel-Hashes, einmal mit String- und einmal mit Symbol-Schlüsseln:

>> laender = { 'de' => 'Deutschland',
?> 'fr' => 'Frankreich',
?> 'uk' => 'United Kingdom' }
=> {"uk"=>"United Kingdom", "fr"=>"Frankreich",
de"=>"Deutschland"}
>> person = { :name => 'Schmitz',
?> :vorname => 'Peter',
?> :geburtsdatum => '1972-07-12' }
=> {:name=>"Schmitz", :vorname=>"Peter",
geburtsdatum=>"1972-07-12"}

Der Zugriff auf die Werte eines Hashs erfolgt durch Angabe des Schlüssels in eckigen Klammern:

>> puts "#{person[:vorname]} wohnt in #{laender['de']}"
Peter wohnt in Deutschland

Die arithmetischen Operatoren entsprechen im Wesentlichen denjenigen in den bereits gezeigten Sprachen; hinzu kommt ** für die Potenzierung. Die Vergleichsoperatoren wie ==, != oder < gelten in Ruby sowohl für Zahlen als auch für Strings. Zusätzlich gibt es den Operator ===, der als linken Operanden einen Bereich und als rechten Operanden ein mögliches Element erwartet. Er liefert true, falls das Element darin vorkommt, und andernfalls false:

>> (1..10) = = = 5
=> true
>> ('a'...'m') = = = 'm'
=> false

Um das Ergebnis zu verstehen, sollten Sie noch wissen, dass zwei Punkte einen Bereich einschließlich des letzten Elements bilden, während es bei drei Punkten nicht mehr dazugehört.

Die logischen Operatoren gibt es in zwei Varianten: in Zeichenform als &&, || und ! sowie in Textschreibweise als and, or und not. Letztere haben die niedrigste Priorität aller Ruby-Operatoren, werden also in einem Ausdruck zuletzt ausgewertet; zudem können and und or als Ersatz für Fallunterscheidungen eingesetzt werden. Das folgende Beispiel gibt eine Meldung aus, wenn die Variable alter kleiner als 18 ist:

alter < 18 and puts "Sorry, der Film ist ab 18!"

Dies lässt sich auch umgekehrt mit or schreiben:

alter >= 18 or puts "Sorry, der Film ist ab 18!"

Im Bereich der Zuweisungsoperatoren stehen Kurzfassungen wie += und *= zur Verfügung, aber kein ++ und --. Dafür gibt es die Methode succ, die den unmittelbaren Nachfolger eines Elements liefert – nicht nur einer Ganzzahl, sondern auch eines Strings:

>> 41.succ
=> 42
>> "abc".succ
=> "abd"
>> "z".succ
=> "aa"

Wichtige eingebaute Standardmethoden

Als objektorientierte Sprache bietet Ruby die meisten Methodenaufrufe in der Form objekt.methode(...) an, wobei die betroffenen Objekte nicht nur Variablen, sondern auch Literale oder Ausdrücke sein können. Für Zahlen stehen beispielsweise die Methoden abs (Betrag), round (zur Ganzzahl gerundeter Wert), ceil (nächsthöhere Ganzzahl) und floor (nächstniedrigere Ganzzahl) zur Verfügung:

>> –2.abs
=> 2
>> 3.1.round
=> 3
>> 3.1.ceil
=> 4
>> 3.ceil
=> 3

Weitere mathematische Methoden stellt das Modul Math bereit. Ein Modul ist eine Art Klasse mit den Besonderheiten, dass sie nur Klassenmethoden besitzt, nicht instanziiert und auch nicht erweitert werden kann. Math enthält unter anderem die Konstanten Math::E (die Eulersche Zahl e) und Math::PI (p). Zu den Methoden gehören etwa die trigonometrischen Funktionen, Logarithmus oder Quadratwurzel. Hier als Beispiele der Sinus von p/2 (90° im Bogenmaß), der natürliche Logarithmus von e und die Quadratwurzel von 2:

>> Math.sin(Math::PI / 2)
=> 1.0
>> Math.log(Math::E)
=> 1.0
>> Math.sqrt(2)
=> 1.4142135623731

Bei vielen Ruby-Methoden können Sie die Klammern um die Argumente weglassen. Dies soll allerdings in künftigen Versionen eingeschränkt werden und wird bereits kritisiert, wenn Sie den Interpreter mit der Option -w (Warnungen) starten – eine gängige Ausnahme sind Kontrollstrukturen und I/O-Methoden, was auch in diesem Abschnitt berücksichtigt wird. Falls Sie sich für Klammern entscheiden, ist es wichtig, darauf zu achten, dass zwischen dem Methodennamen und der öffnenden Klammer kein Leerzeichen stehen darf.

Auch für Strings stehen zahlreiche interessante Methoden zur Verfügung. Hier nur einige im Überblick, jeweils mit Beispiel und erläuterndem Kommentar:

>> text = "Ruby macht Spass"
=> "Ruby macht Spass"
>> text.upcase # alles gross
=> "RUBY MACHT SPASS"
>> text.downcase # alles klein
=> "ruby macht spass"
>> text.swapcase # vertauschen
=> "rUBY MACHT sPASS"
>> text.reverse # umkehren
=> "ssapS thcam ybuR"
>> text.length # Zeichenanzahl
=> 16

Alle String-Manipulationsmethoden gibt es übrigens noch ein zweites Mal: mit angehängtem Ausrufezeichen. In dieser Fassung modifizieren sie den Inhalt der angesprochenen Variablen direkt, anstatt nur den entsprechenden Wert zurückzuliefern. Sie dürfen nicht für Literale aufgerufen werden. Hier ein Beispiel mit reverse:

>> satz = "This is not Klingon"  # Ursprungstext
=> "This is not Klingon"
>> satz.reverse # umkehren
=> "nognilK ton si sihT"
>> satz # Variable prüfen
=> "This is not Klingon"
>> satz.reverse! # dauerhaft umkehren
=> "nognilK ton si sihT"
>> satz # überprüfen
=> "nognilK ton si sihT"

Eine weitere Besonderheit von Ruby-Strings besteht darin, dass Sie per Index auf sie zugreifen können – und zwar nicht nur lesend, sondern sogar zur Wertzuweisung. Als Index kommen dabei Ganzzahlen für einzelne Zeichen, Bereiche oder sogar Strings infrage. Beispiele:

>> text = "Perl macht Spass"
=> "Perl macht Spass"
>> text[0]
=> P
>> text[0...4]
=> "Perl"
>> text["Perl"] = "Ruby"
=> "Ruby"
>> text
=> "Ruby macht Spass"

In älteren Ruby-Versionen bis 1.8 liefert der Zugriff auf ein einzelnes Zeichen dessen Code. text[0] würde hier also 80 liefern. Die Methode chr konvertiert den Code wieder in ein Zeichen:

>> 80.chr
=> "P"

Da viele Operatoren in Ruby ebenfalls Methoden sind, die sich sogar per Methodendefinition hinzufügen oder manipulieren lassen, werden auch sie hier besprochen. Neben dem bereits gezeigten + zur String-Verkettung können Sie * verwenden, um den Inhalt eines Strings zu vervielfachen; der zweite Operand muss dabei eine Ganzzahl sein. Beispiel:

>> "#" * 20
=> "####################"

Dieser Operator steht übrigens auch für Arrays zur Verfügung, deren Methoden als Nächstes betrachtet werden. Beispiel:

>> [1, 2, 3] * 2
=> [1, 2, 3, 1, 2, 3]

Arrays besitzen auch die Operatoren + und -, um Vereinigungsmengen beziehungsweise Restmengen zu bilden. Beispiele:

>> arr = ['a', 'b', 'c', 'd']
=> ["a", "b", "c", "d"]
>> arr + ['e', 'f']
=> ["a", "b", "c", "d", "e", "f"]
>> arr
=> ["a", "b", "c", "d"]
>> arr += ['e']
=> ["a", "b", "c", "d", "e"]
>> arr - ['c', 'e', 'g']
=> ["a", "b", "d"]

Zum Hinzufügen und Entfernen von Elementen an beiden Array-Enden stehen dieselben Methoden wie in Perl zur Verfügung: push fügt Elemente am Ende hinzu, pop entfernt das letzte Element und liefert es zurück, unshift fügt Elemente am Anfang ein, und shift nimmt das erste Element weg:

>> todo = ['Festplatte formatieren', 'System
installieren',
'Grundkonfiguration']
=> ["Festplatte formatieren", "System installieren",
"Grundkonfiguration"]
>> todo.unshift('Festplatte partitionieren')
=> ["Festplatte partitionieren", "Festplatte
formatieren", "System installieren",
"Grundkonfiguration"]
>> todo.push('Anwendungen installieren')
=> ["Festplatte partitionieren", "Festplatte
formatieren", "System installieren",
"Grundkonfiguration", "Anwendungen installieren"]
>> nextjob = todo.shift
=> "Festplatte partitionieren"
>> dropjob = todo.pop
=> "Anwendungen installieren"
>> todo
=> ["Festplatte formatieren", "System installieren",
"Grundkonfiguration"]

Hier einige weitere Array-Methoden:

  • sort sortiert die Elemente des Arrays, sofern alle denselben Typ besitzen, standardmäßig aufsteigend und je nach Elementtypen in numerischer oder in Zeichensatzreihenfolge:
    >> ["Zebra", "Baer", "Giraffe", "Affe"].sort
    => ["Affe", "Baer", "Giraffe", "Zebra"]
    >> [1, 10, 2, 11, 3].sort
    => [1, 2, 3, 10, 11]
    >> ["1", "2", "11", "3", "10"].sort
    => ["1", "10", "11", "2", "3"]
  • uniq entfernt alle Duplikate:
    >> %w(eins zwei zwei drei drei drei).uniq
    => ["eins", "zwei", "drei"]
  • reverse kehrt die Elementreihenfolge um:
    >> %w(lesen andersherum bitte Satz Diesen).reverse
    => ["Diesen", "Satz", "bitte", "andersherum", "lesen"]

Ruby bietet auch diverse Methoden zur Typkonvertierung an. Zum Beispiel wandelt die Methode to_s Ganz- oder Fließkommazahlen in Strings um. Bei Ganzzahlen können Sie optional ein Zahlensystem zwischen 2 und 36 angeben, in das die Zahl vor der String-Konvertierung umgerechnet werden soll:

>> 2.5.to_s
=> "2.5"
>> 47.to_s
=> "47"
>> 47.to_s(8)
=> "57"
>> 47.to_s(16)
=> "2f"

to_i wandelt diverse Objekte in Ganzzahlen um. Fließkommazahlen werden dabei ohne Rücksicht auf den Wert der Dezimalstellen abgeschnitten. Bei Strings kann wieder ein Zahlensystem angegeben werden, diesmal als Quelle und nicht als Ziel der Konvertierung. Beispiele:

>> "99".to_i
=> 99
>> 2.9.to_i
=> 2
>> "ff".to_i(16)
=> 255
>> "zz".to_i(36)
=> 1295
>> "10110111011110".to_i(2)
=> 11742

Um Fließkommazahlen mathematisch korrekt zu runden, wird dagegen die Methode round verwendet:

>> 2.49.round
=> 2
>> 2.5.round
=> 3

Arrays lassen sich mithilfe der Methode join in Strings umwandeln. Als Argument können Sie eine Zeichenfolge angeben, durch die die Elemente getrennt werden sollen:

>> wochentage = %w(Mo Di Mi Do Fr Sa So)
=> ["Mo", "Di", "Mi", "Do", "Fr", "Sa", "So"]
>> wochentage.join(', ')
=> "Mo, Di, Mi, Do, Fr, Sa, So"

Die Methode split erledigt das Gegenteil. Wenn Sie kein Argument angeben, erhalten Sie ein Array aus einzelnen Zeichen; andernfalls können Sie einen String oder einen regulären Ausdruck angeben, bei dessen Vorkommen der String zerlegt werden soll. Das folgende Beispiel extrahiert einzelne Wörter, indem es ein oder mehrere Nicht-Wort-Zeichen als Grenze verwendet (Details zur Syntax regulärer Ausdrücke erfahren Sie im nächsten Kapitel):

>> satz = "Hallo! Computer, bitte zerlege diesen Satz."
=> "Hallo! Computer, bitte zerlege diesen Satz."
>> satz.split(/\W+/)
=> ["Hallo", "Computer", "bitte", "zerlege", "diesen",
"Satz"]

Kontrollstrukturen

Die wichtigste Fallunterscheidung heißt auch in Ruby if. Die Bedingung steht im Allgemeinen nicht in Klammern. Die von der Fallunterscheidung abhängigen Anweisungen werden nicht durch geschweifte Klammern umschlossen, sondern stehen zwischen if und end. Das folgende Beispiel prüft, ob die Variable zahl eine gerade Zahl enthält und gibt eine entsprechende Meldung aus:

if zahl % 2 == 0
puts "#{zahl} ist gerade"
end

Ruby kennt auch ein nachgestelltes if wie in Perl, das Sie immer dann einsetzen können, wenn nur eine einzige Anweisung von der Bedingungsprüfung abhängen soll:

puts "#{zahl} ist gerade" if zahl % 2 == 0

Die von Perl inspirierte Fallunterscheidung unless, das heißt die Umkehrung von if, ist ebenfalls verfügbar.

Einen eigenen Weg geht Ruby bei der case-Fallunterscheidung. Das Start-Schlüsselwort heißt nicht switch, sondern case. Die einzelnen Fälle werden mit when gebildet und können mehrere durch Komma getrennte Vergleichswerte auflisten. Sie brauchen nicht mithilfe von break abgeschlossen zu werden, sondern enden beim nächsten when oder bei else, das für die restlichen Fälle eingesetzt wird. Hier ein kleines Quiz als Komplettbeispiel:

puts "Wie heisst eine gute objektorientierte
Skriptsprache aus Japan?"
puts "a) Sapphire"
puts "b) Diamond"
puts "c) Ruby"
puts "d) Amethyst"
print "Ihre Wahl? "
auswahl = gets.chomp
case auswahl
when 'a', 'A'
puts "Nein -- obwohl der Rubin eine spezielle rote
Unterart der Saphire ist"
when 'b', 'B'
puts "Ein Diamant ist es auch nicht"
when 'c', 'C'
puts "Ja, richtig!"
when 'd', 'D'
puts "Nein, leider falsch."
else
puts "Ungueltige Eingabe!"
end

Bei den Schleifen haben Sie die Auswahl zwischen while und seinem Gegenteil until, das ausgeführt wird, solange die Bedingung noch nicht wahr ist. Beide können sowohl mehrere Anweisungen einleiten, die mit end abgeschlossen werden, als auch hinter eine einzelne Anweisung geschrieben werden. Beispiele:

# Bis 10 zählen, while-Variante
i = 1
while i <= 10
puts "#{i}. Durchgang"
i += 1
end

# Bis 9(!) zählen, until-Variante
i = 1
until i == 10
puts "#{i}. Durchgang"
i += 1
end

# Nachgestelltes while
print "Noch ein Versuch (j/n)? "
w = gets.chomp.downcase while w != 'j' && w != 'n'

Recht häufig wird in Ruby auch die Endlosschleife mit definiertem Abbruch eingesetzt:

loop do
# Schleifenrumpf
# Abbruchbedingung
break if Bedingung
end

for-Schleifen gibt es in Ruby nur in der Variante for ... in zur Bearbeitung aller Elemente eines Arrays, Hashs oder Bereichs:

# Bis 10 zählen, for-Variante
for i in (1..10)
puts i
end

# Alle Elemente eines Arrays verarbeiten
Vokale = %w(a e i o u)
for v in vokale
puts "#{v} ist ein Vokal"
end

Für eine for-Schleife über die Elemente eines Hashs wird die Methode keys verwendet:

wochentage = {'Mo' => "Montag", 'Di' => "Dienstag",
'Mi' => "Mittwoch", 'Do' => "Donnerstag",
'Fr' => "Freitag", 'Sa' => "Samstag",
'So' => "Sonntag"}
for w in wochentage.keys
puts "#{wochentage[w]} hat die Abkuerzung #{w}"
end

Bei diesem Beispiel werden Sie übrigens feststellen, dass die Ausgabereihenfolge nicht Ihrer ursprünglichen Festlegung entspricht. Ruby optimiert die Speicherung des Hashs und garantiert dabei keine bestimmte Reihenfolge. Es besteht aber beispielsweise die Möglichkeit, den Hash nach Schlüsseln alphabetisch zu sortieren, indem Sie hash.keys.sort in den Kopf der for-Schleife schreiben. Dadurch wird der Hash allerdings in ein verschachteltes Array umgewandelt, was in anderen Fällen zu Problemen führen kann.

Iteratoren

Iteratoren sind vielleicht das praktischste Ruby-Feature. Genau wie die for-Schleifen erlauben sie die Verarbeitung aller Elemente einer Menge, allerdings nicht beschränkt auf bloße Aufzählung.

Jeder Iterator wird in folgender Form verwendet (eine der wenigen Verwendungen geschweifter Klammern in Ruby):

Objekt.Iteratormethode {
# Iteratorrumpf
}

Oder alternativ:

Objekt.Iteratormethode do
# ...
end

Der Teil zwischen { und } beziehungsweise do und end wird als Block bezeichnet und hat besondere Eigenschaften. Im Grunde können Sie sich einen Block als eine Art Callback vorstellen, das für jeden Durchlauf vom Iterator aus aufgerufen wird.

Für Objekte verschiedener Klassen stehen unterschiedliche Iteratoren bereit. Der einfachste von allen ist für Ganzzahlen definiert und heißt times. Er führt eine Aufgabe n-mal durch. Das folgende Beispiel zeigt den Kölner Karnevalsruf:

3.times {
puts "Koelle Alaaf!"
}

Viele Iteratoren setzen in jedem Durchlauf einen Zähler, andere dynamische Werte. Um diese zu verwenden, können Sie zu Beginn des Iteratorrumpfs eine entsprechende Anzahl von Variablen zwischen zwei Pipe-Zeichen (|) setzen. Im Fall von times wird von 0 bis n–1 gezählt. Das folgende Beispiel gibt daher die Werte von 20 bis 215 aus:

16.times { |i|
printf "2 ** %2d = %5d\n", i, 2 ** i
}

Weitere Ganzzahl-Iteratoren sind unter anderem:

  • m.upto(n) zählt in Einerschritten von m bis zur größeren Zahl n.
  • m.downto(n) zählt dagegen abwärts, wobei m größer als n sein muss.
  • m.step(n, s) zählt in Schritten der Weite s von m bis n.

Der wichtigste Iterator für Arrays und Bereiche ist each. Er geht jedes Element des Objekts nacheinander durch. Beispiel:

jahreszeiten = %w(Fruehling Sommer Herbst Winter)
jahreszeiten.each { |j|
puts "Jahreszeit #{j}"
}

Für Hashes gibt es die Iteratoren each_key, each_value und each_pair, die jeden Schlüssel, jeden Wert beziehungsweise jedes Schlüssel-Wert-Paar verarbeiten. Für Letzteres brauchen Sie zwei Variablen:

sprachen = {'de' => "Deutsch", 'en' => "Englisch",
'fr' => "Franzoesisch", 'es' => "Spanisch"}
sprachen.each_pair { |kurz, lang|
puts "#{kurz} steht fuer #{lang}"
}

Rheinwerk Computing - Zum Seitenanfang

9.4.3 Objektorientierung in RubyZur nächsten ÜberschriftZur vorigen Überschrift

Im Gegensatz zu Java verwendet Ruby grundsätzlich offene Klassen – ihre Methoden lassen sich also im Nachhinein ändern. Dies gilt sogar für die eingebauten Klassen des Ruby-Sprachkerns. In diesem Abschnitt werden zunächst einige Standardklassen vorgestellt; danach erfahren Sie, wie Sie selbst Klassen erstellen und modifizieren.

Wichtige eingebaute Klassen

Funktionen wie Ein- und Ausgabe, Datum und Uhrzeit, erweiterte reguläre Ausdrücke und so weiter werden in Ruby durch Klassen bereitgestellt. Dabei kommen sowohl Instanzen dieser Klassen und deren Instanzmethoden als auch Klassenmethoden zum Einsatz.

Die Ruby-Ein- und Ausgabemethoden sind Instanzmethoden der Basisklasse IO oder von ihr abgeleiteter Klassen. Deshalb funktioniert der Zugriff auf die Konsole, auf Dateien und Verzeichnisse sowie auf Netzwerkverbindungen ungefähr gleich. Eingangs haben Sie bereits einige I/O-Methoden gesehen, die allerdings ohne Objektbezug aufgerufen wurden. Diese für manche Methoden verfügbare Kurzschreibweise wählt automatisch das jeweils aktuelle Ein- oder Ausgabeobjekt aus, normalerweise also die Standardeingabe oder -ausgabe. Sie können STDIN, STDOUT und STDERR aber auch jeweils explizit hinschreiben; wenn mehrere I/O-Kanäle geöffnet sind, müssen Sie das manchmal sogar.

Wichtige Ausgabemethoden sind print zur Ausgabe beliebiger Strings, puts zur Ausgabe eines Strings mit nachfolgendem Leerzeichen sowie printf zur formatierten Ausgabe. Abgesehen von der objektorientierten Schreibweise und der Möglichkeit, eingebettete Ausdrücke zu verwenden, entsprechen diese Methoden ihren zuvor vorgestellten Pendants in C. Daher hier nur einige Beispiele:

>> print "Ihr Name? "; name = gets
Ihr Name? Sascha
=> "Sascha\n"
>> puts "Zeile 1", "Zeile 2 mit eingebettetem
Ausdruck:
#{3 * 7 + 2}"
Zeile 1
Zeile 2 mit eingebettetem Ausdruck: 23
>> printf "%.2f + %.2f = %.2f\n", 3, 4, 3 + 4
3.00 + 4.00 = 7.00

Das erste Beispiel enthält bereits die wichtigste Eingabemethode, gets. Sie liest eine Zeile ein und liefert sie samt Zeilenumbruch zurück. Den Zeilenumbruch können Sie wie in Perl durch die Methode chomp entfernen; am einfachsten rufen Sie gleich gets.chomp auf:

>> eingabe = gets.chomp
Hallo
Welt
=> "Hallo Welt"

Die andere interessante Eingabemethode ist read. Sie liest beliebig viel Text bis EOF (End of File, ein spezielles Zeichen, das am Dateiende gesendet wird) und eignet sich so vor allem zum Einlesen ganzer Dateien. Um sie auf der Konsole auszuprobieren, muss der Eingabekanal STDIN (oder synonym $stdin) explizit angegeben werden. Das Dateiende können Sie dabei künstlich durch die Tastenkombination Strg + D (unter Unix) beziehungsweise Strg + Z (unter Windows) erzeugen. Beispiel:

>> text = STDIN.read
Hallo
Welt!
^Z

=> "Hallo\nWelt!\n"

Dateien werden über die Klasse File verarbeitet. Um auf eine Datei zuzugreifen, wird zunächst eine Instanz dieser Klasse erzeugt; die erforderlichen Argumente sind Dateipfad und Modus. Der Modus entspricht dabei der C-Bibliotheksfunktion fopen(), kann also unter anderem "r", "w" oder "a" sein. Das folgende Beispiel erzeugt im aktuellen Verzeichnis eine Datei namens test.txt zum Schreiben, schreibt eine Zeile Text hinein und schließt sie wieder (Achtung! Der Modus "w" überschreibt eine eventuell vorhandene Datei dieses Namens):

file = File.new("test.txt", "w")
file.puts "Inhalt der Datei"
file.close

Das Lesen der entsprechenden Datei und die Ausgabe ihres Inhalts erfolgen danach so:

file = File.new("test.txt", "r")
lines = file.read
STDOUT.puts lines
file.close

Bevor Sie eine Datei zum Lesen öffnen, sollten Sie überprüfen, ob sie existiert, weil ansonsten eine für Benutzer Ihres Programms untaugliche Fehlermeldung ausgegeben wird. Den Test erledigt die Klassenmethode File.exist?(Pfad). Beispiel:

if File.exist?("test.txt")
# Datei öffnen und verarbeiten
else
puts "Datei existiert nicht"
end

Die Klasse Dir ermöglicht den Lesezugriff auf Verzeichnisse. Als Argument des Konstruktors wird der gewünschte Verzeichnispfad übergeben. Die Methode read liest dann jeweils einen Eintrag, dessen Typ Sie mit statischen Methoden wie File.file? oder File.directory? ermitteln können.

Das folgende Skript liest einen Verzeichnispfad von der Kommandozeile oder verwendet hilfsweise das aktuelle Verzeichnis ".". Anschließend werden die Verzeichniseinträge dreispaltig mit einem der Präfixe D (Verzeichnis), F (gewöhnliche Datei) oder ? (Sonstiges) ausgegeben:

dirname = "."
# Verzeichnis von der Kommandozeile lesen,
# falls möglich
dirname = ARGV[0] if ARGV[0] != nil
# Block zum Abfangen von Exceptions
# Verzeichnis öffnen
dir = Dir.new(dirname)
# Array für Einträge
entries = []
# Alle Einträge auslesen
while entry = dir.read
# Prüfen und in Array speichern
path = "#{dirname}/#{entry}"
if File.directory?(path)
type = "D"
elsif File.file?(path)
type = "F"
else
type = "?"
end
entries.push [entry, type]
end
dir.close
# Längsten Eintrag ermitteln
len = 0
entries.each { |e|
len = e[0].length if e[0].length > len
}
# Spaltenzähler
tab = 0
entries.each { |e|
printf "%s %s ", e[1], e[0].ljust(len)
tab += 1
if tab > 2
puts
tab = 0
end
}

Die meisten in diesem Skript verwendeten Konstrukte wurden bereits erläutert. Zur Speicherung jedes Eintrags und seines Typs wird ein zweidimensionales Array verwendet. Die String-Methode ljust dient dazu, einen String mithilfe von Leerzeichen auf die angegebene Breite zu strecken. Hier eine Beispielausgabe des Skripts:

D .                 D ..                F anleitung.txt
F dir.rb F hello.rb F info.txt
D misc F script.rb D scripts
F test.txt F verzeichnisse.rb

Datum und Uhrzeit werden in Ruby über die Klasse Time bereitgestellt. Ein Konstruktoraufruf ohne Argumente kapselt die aktuelle Systemzeit. Die wichtigste Methode ist strftime; sie nimmt einen Formatstring entgegen, der die gewünschten Bestandteile der Zeitangabe ausliest und formatiert. Die verfügbaren Formatkürzel entsprechen der gleichnamigen C-Funktion beziehungsweise dem in Kapitel 7, »Linux«, vorgestellten Unix-Dienstprogramm date. Hier ein Beispiel:

t = Time.new
puts t.strftime("%d.%m.%Y, %H:%M")

Die Ausgabe lautet zum Beispiel:

13.04.2013, 16:05

Daneben besitzen Time-Objekte die Methoden sec, min, hour, day, month und year, um die einzelnen Bestandteile direkt auszulesen.

Falls Sie einen anderen Zeitpunkt als die aktuelle Systemzeit speichern möchten, können Sie die Bibliothek time importieren und dann die Klassenmethode Time.parse aufrufen. Ihr Argument ist ein String, etwa im Format "YYYY-MM-DD hh:mm:ss". Hier ein Anwendungsbeispiel:

require "time"
gestern = Time.parse("2013-04-12 16:06:00")

Interessant ist, dass Sie zu einem Datum beliebige Sekundenanzahlen addieren oder davon abziehen können. Das Ergebnis ist wieder eine Time-Instanz mit dem veränderten Datum, wie die irb-Ausgabe zeigt:

>> heute = Time.new
=> Sat Apr 13 16:18:36 +0200 2013
>> morgen = heute + 86400
=> Sun Apr 14 06:18:36 +0200 2013

Sie können Daten sogar voneinander abziehen. Das Ergebnis ist die Differenz in Sekunden, die sich dann weiter umrechnen lässt. Beispiel:

heute = Time.new
frueher = Time.parse("2013-03-11 14:16:04")
diff = heute - frueher
tage = (diff/86400).to_i
diff -= tage * 86400
stunden = (diff/3600).to_i
diff -= stunden * 3600
minuten = (diff/60).to_i
printf "Die Differenz zwischen %s und %s betraegt %d
Tage, %d Stunden und %d Minuten\n", heute, frueher,
tage, stunden, minuten

Die Ausgabe lautet:

Die Differenz zwischen Sat Apr 13 06:21:04 +0200 2013
und Sun Mar 11 04:16:04 +0100 2013 betraegt 33 Tage,
1 Stunden und 5 Minuten

Obwohl die Uhrzeiten 13:06 Uhr und 11:06 Uhr gewählt wurden, beträgt die Differenz nach Abzug der Tage eine Stunde. Das liegt daran, dass zwischen den beiden Daten die Umstellung auf Sommerzeit folgte – die UTC-Differenz im internen Datumsanzeigeformat (+0200 beziehungsweise +0100) zeigt dies deutlich.

In einem für Endbenutzer geschriebenen Programm sollten Sie sich übrigens noch darum kümmern, dass beim Wert 1 jeweils der Singular (»1 Stunde«) ausgegeben wird. Beispielsweise so:

printf "%d Stunde%s\n", stunden, (stunden != 1 ? "n", "")

Klassen implementieren und modifizieren

Ruby macht es Ihnen sehr leicht, eigene Klassen zu schreiben. Sie können zusammen mit anderem Code in Dateien mit beliebigem Namen stehen. Allerdings ist es bei größeren Projekten durchaus sinnvoll, jede Klasse in eine eigene Datei mit dem Namen Klassenname.rb zu speichern. Wenn Sie Code auslagern, können Sie ihn (sinnvollerweise ganz am Anfang der fraglichen Datei) wie folgt einbinden:

require "meineklasse.rb"

Eine Klasse wird von class Klassenname und end umschlossen. Darin werden Methoden definiert, die wiederum zwischen def Methodenname und end stehen. Der eigentliche Konstruktor ist eine Methode namens new. Diese ist jedoch in der Urklasse Object fest definiert und kann nicht überschrieben werden. Zur Initialisierung der Instanzvariablen können Sie stattdessen eine Methode namens initialize schreiben, die bei der Instanziierung automatisch aufgerufen wird.

Das erste Beispiel ist eine sehr einfache Klasse, die den Vor- und Nachnamen eines Buchautors kapselt:

class Autor
# Akzessoren
attr_accessor :name, :vorname

# Konstruktor
def initialize(name, vorname)
@name = name
@vorname = vorname
end

# String-Darstellung der Instanz
def to_s
"#{@vorname} #{@name}"
end
end

Hier sind einige Erläuterungen erforderlich, da Ruby vieles anders macht als Java: Zunächst fällt vielleicht auf, dass keine Getter- und Setter-Methoden für die Eigenschaften vereinbart wurden. Ruby bietet mit den sogenannten Akzessoren eine viel bequemere Möglichkeit dafür: Jede Instanzvariable, deren Namen Sie als Symbol in die Liste hinter attr_accessor schreiben, kann als Instanz.Variable ausgelesen und mithilfe von = verändert werden. Soll eine Variable dagegen nur gelesen werden können, verwenden Sie attr_reader. Um einen Setter mit Wertüberprüfung zu erstellen, können Sie stattdessen eine Methode namens Instanzvariable= (ohne führendes @-Zeichen) definieren.

Der Konstruktor, repräsentiert durch die Methode initialize, dient wie üblich der Initialisierung der Attribute.

Interessant ist die Methode to_s: Wenn Sie eine Instanz der Klasse im String-Kontext verwenden, also beispielsweise puts Instanz schreiben, wird sie automatisch aufgerufen, um die »offizielle« String-Entsprechung der Instanz zu erhalten. Wenn Sie keine to_s-Methode definieren, wird stattdessen eine relativ nichtssagende Kombination aus dem Klassennamen und der Objekt-ID (einer eindeutigen positiven Ganzzahl) ausgegeben.

Erwähnenswert ist noch, dass diese Methode ohne return auskommt: In Ruby können Sie einen Ausdruck als vollständige Anweisung schreiben; falls die betreffende Zeile die letzte ist, die in einer Methode ausgeführt wird, gibt die Methode den Wert des Ausdrucks zurück. Alternativ ist aber auch ein explizites return möglich.

Eine Instanz der Klasse Autor wird wie folgt erzeugt:

adams = Autor.new("Adams", "Douglas")

Die Ausgabeanweisung

puts adams

liefert daraufhin Folgendes:

Douglas Adams

Probieren Sie auch einen Akzessor aus, indem Sie den zweiten Vornamen hinzufügen:

adams.vorname = "Douglas Noel"

Dies liefert die Ausgabe

Douglas Noel Adams

Da Ruby keine typisierte Sprache ist, ist es nicht möglich, Konstruktoren oder Methoden zu überladen. Stattdessen können Sie den Parametern bei der Methodendefinition Default-Werte zuweisen, die automatisch verwendet werden, falls weniger Argumente übergeben wurden. Das folgende Beispiel kapselt ein Thema mit optionalem Oberthema und ermöglicht so hierarchische Themenangaben für Fachbücher wie »EDV > Datenbanken > MySQL«:

class Thema
# Akzessoren
attr_accessor :titel, :oberthema

#Konstruktor
def initialize(titel, oberthema = nil)
@titel = titel
@oberthema = oberthema
end

# String-Entsprechung
def to_s
ausgabe = ""
if (@oberthema != nil)
# Oberthema rekursiv aufrufen
ausgabe += @oberthema.to_s + " > "
end
ausgabe += @titel
end
end

Wie Sie sehen, besitzt der Konstruktor-Parameter oberthema den Standardwert nil (leere Objektreferenz). Probieren Sie die Instanziierung aus, um eine Themenhierarchie zu erstellen – zuerst einmal ohne und dann mit Oberthema:

edv  = Thema.new("EDV")
prog = Thema.new("Programmiereung", edv)
ruby = Thema.new("Ruby", prog)
puts ruby

Die Ausgabe lautet:

EDV > Programmiereung > Ruby

Noch praktischer, als Standardwerte zu verwenden, ist manchmal ein Hash als Konstruktor- oder Methodenargument. Er erlaubt Ihnen, die Methode mit benannten Parametern in beliebiger Anzahl aufzurufen. Das folgende Beispiel kapselt ein Buch unter Verwendung der zuvor definierten Klasse Autor und zeigt allerlei zusätzliche Ruby-OO-Tricks:

class Buch

# Akzessoren
attr_accessor :isbn, :titel, :verlag,
:ort, :jahr, :preis
attr_reader :autoren


# Konstruktor
def initialize(hash)
@autoren = []
if hash[:isbn]
@isbn = hash[:isbn]
end
if hash[:titel]
@titel = hash[:titel]
end
if hash[:autoren]
if hash[:autoren].class == Array
@autoren = hash[:autoren]
else
@autoren.push(hash[:autoren])
end
end
if hash[:verlag]
@verlag = hash[:verlag]
end
if hash[:ort]
@ort = hash[:ort]
end
if hash[:jahr]
@jahr = hash[:jahr]
end
if hash[:preis]
@preis = hash[:preis]
end
end

# Weiteren Autor hinzufügen
def add_autor(autor)
@autoren.push(autor)
end

# Autor entfernen
def remove_autor(autor)
@autoren.each_with_index { |a, i|
if autor.name == a.name &&
autor.vorname == a.vorname
@autoren.slice!(i)
end
}
end

# String-Entsprechung
def to_s
ausgabe = ""
@autoren.each_with_index { |a, i|
ausgabe += ", " unless i == 0
ausgabe += a.to_s
}
ausgabe += ": \"#{@titel}\"\n"
ausgabe += "#{@ort} #{@jahr}, #{@verlag}\n"
ausgabe += "ISBN: #{@isbn}, "
ausgabe += sprintf "%.2f EUR\n", @preis
ausgabe
end
end

Beachten Sie zunächst, dass die Instanzvariable @autoren durch die Vereinbarung attr_reader von außen nur lesbar ist. Es handelt sich um ein Array von Autor-Objekten, ist also viel zu komplex, um gefahrlos manipuliert zu werden.

Der Konstruktor initialisiert @autoren zunächst als leeres Array. Anschließend prüft er, ob die diversen Hash-Schlüssel existieren. Falls dem so ist, werden den jeweiligen Instanzvariablen die zugehörigen Werte zugewiesen. Im Fall der Autoren wird sogar zwischen einem einzelnen Autor und einem Array von Autoren unterschieden; die Überprüfung erledigt die Methode class.

Die Methode add_autor ist sehr einfach: Sie ergänzt @autoren mithilfe von push um den übergebenen Autor. remove_autor ist wesentlich komplizierter: Da bei einer übergebenen Instanz von Autor nicht erwartet werden kann, dass es sich um dasselbe Objekt handelt, müssen Vor- und Nachname verglichen werden. Der praktische Array-Iterator each_with_index liefert dazu jeden Wert des Arrays mitsamt seinem Index. Letzteren kann die Methode slice! im Fall eines Treffers verwenden, um den gewünschten Autor aus dem Array zu entfernen.

Auch in der Methode to_s kommt each_with_index zum Einsatz, um allen Autoren außer dem ersten ein Komma voranzustellen. Der Rest der Methode dürfte ohne Weiteres verständlich sein.

Das folgende Beispiel erzeugt einen Autor und ein Buch dieses Autors:

adams       = Autor.new("Adams", "Douglas Noel")

anhalter = Buch.new(:isbn => '3-45314-697-2',
:titel =>
'Per Anhalter durch die Galaxis',
:verlag => 'Heyne',
:ort => 'Muenchen',
:jahr => 1998,
:preis => 6.95,
:autoren => adams)

Die Anweisung puts anhalter liefert daraufhin Folgendes:

Douglas Noel Adams: "Per Anhalter durch die Galaxis"
Muenchen 1998, Heyne
ISBN: 3-45314-697-2, 6.95 EUR

Für die Vererbung wird in Ruby der Operator < verwendet. Das folgende Beispiel leitet von Buch eine Klasse namens Fachbuch ab, die eine Instanz von Thema als weiteres Attribut hinzufügt:

class Fachbuch < Buch
attr_accessor :thema

def initialize(hash)
super(hash)
if hash[:thema]
@thema = hash[:thema]
end
end

def to_s
super.to_s + "Thema: " + @thema.to_s
end
end

Der Aufruf von super greift wie in Java auf die Elternklasse zu. Hier ein Verwendungsbeispiel:

niedermeier = Autor.new("Niedermeier", "Stephan")
scholz = Autor.new("Scholz", "Michael")

edv = Thema.new("EDV")
prog = Thema.new("Programmierung", edv)
java = Thema.new("Java", prog)

jxml = Fachbuch.new(:isbn => '3-89842-646-7',
:titel => 'Java und XML',
:verlag => 'Galileo Press',
:ort => 'Bonn',
:jahr => 2005,
:preis => 39.90,
:thema => java,
:autoren => [niedermeier, scholz])

Die entsprechende Ausgabe sieht wie folgt aus:

Stephan Niedermeier, Michael Scholz: "Java und XML"
Bonn 2005, Galileo Press
ISBN: 3-89842-646-7, 39.90 EUR
Thema: EDV > Programmierung > Java

Eigene Iteratoren schreiben Sie, indem Sie in einer Methode das Schlüsselwort yield verwenden. Es ruft den Code in dem Block auf, der mit dem Methodenaufruf verknüpft ist, und übergibt optional die Werte, die in den Variablen zwischen den beiden Pipe-Zeichen gespeichert werden. Die folgende Methode kann zur Klasse Buch hinzugefügt werden; sie geht sämtliche Autoren des Buches durch und übergibt sie an den Block des Methodenaufrufs:

def each_autor
@autoren.each { |autor|
yield autor
}
end

Für das obige Beispiel, die Buch-Instanz jxml, können Sie dann folgenden Aufruf verwenden, um die String-Darstellungen der Autoren auszugeben:

jxml.each_autor { |autor|
puts autor
}

Die Ausgabe sieht natürlich folgendermaßen aus:

Stephan Niedermeier
Michael Scholz

Der Aufruf einer solchen Methode ohne zugehörigen Block scheitert übrigens mit einer Fehlermeldung. Um dies zu vermeiden, können Sie den yield-Aufruf durch das Konstrukt if block_given? absichern – diese Methode liefert nur dann true zurück, wenn der Aufruf mit einem Block erfolgte.

Wie bereits erwähnt, können Sie vorhandene Klassen nachträglich modifizieren. Das folgende Beispiel ergänzt Numeric (die gemeinsame Elternklasse aller Zahlen) um zwei Methoden namens cm_to_in und in_to_cm zur Umrechnung von Zentimetern in Inch und umgekehrt:

class Numeric
def cm_to_in
self / 2.54
end

def in_to_cm
self * 2.54
end
end

self steht in solchen Klassenerweiterungen jeweils für die aktuelle Instanz. Wenn Sie diese Definition in einer Datei (beispielsweise in inch.rb) speichern, können Sie sie mithilfe von

require "inch.rb"

sowohl in ein eigenes Skript als auch in irb laden und sie ausprobieren. Beispiel:

>> require "inch.rb"
=> true
>> 3.in_to_cm
=> 7.62
>> 5.08.cm_to_in
=> 2.0


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück




Copyright © Rheinwerk Verlag GmbH 2013
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern


  Zum Rheinwerk-Shop
Neuauflage: IT-Handbuch für Fachinformatiker






Neuauflage: IT-Handbuch für Fachinformatiker
Jetzt Buch bestellen


 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


Zum Rheinwerk-Shop: Computer Netzwerke






 Computer Netzwerke


Zum Rheinwerk-Shop: Schrödinger lernt HTML5, CSS3 und JavaScript






 Schrödinger lernt
 HTML5, CSS3
 und JavaScript


Zum Rheinwerk-Shop: Windows 8.1 Pro






 Windows 8.1 Pro


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo