Mehr über Methoden

Bisher haben wir ein paar Methoden kennengelernt, puts, gets und so weiter (Spezial-Quiz: Liste alle Methoden auf, die du gesehen hast! Es sind genau 10; die Auflösung gibt es später.) und doch haben wir noch nicht darüber gesprochen, was Methoden denn eigentlich sind. Wir wissen was sie tun, aber wir wissen nicht, was sie sind.

Aber das ist genau das, was sie sind: Dinge, die etwas tun. Wenn Objekte (wie Strings, Integers oder Floats) in der Programmiersprache die Nomen sind, dann sind Methoden die Verben. Und wie im Deutschen, kann man kein Verb verwenden ohne ein Nomen zu haben, das das Verb ausführt. Zum Beispiel ist 'ticken' nicht etwas, das einfach passiert; eine Uhr muss es tun. Im Deutschen heißt das: "die Uhr tickt." In Ruby heißt es uhr.tickt (unter der Annahme, dass uhr ein Ruby-Objekt ist). Programmierer sagen, dass wie die tickt-Methode des Uhr-Objektes aufrufen.
Hast du das Quiz gelöst? Gut. Ich bin sicher, dass du dich an die Methoden puts, gets und chomp erinnert hast, weil wir sie behandelt haben. Du hast wahrscheinlich auf die Umwandlungsmethoden gefunden, to_s, to_i und to_f. Und, hast du die weiteren 4 erkannt? Nichts anderes als unsere alten Mathe-Freunde +, -, *, /!

Ebenso, wie ich sagte, dass jedes Verb ein Nomen braucht, so braucht jede Methode ein Objekt. Normalerweise findet man leicht heraus, welches Objekt eine Methode ausführt: sie ist nämlich genau vor dem Punkt angegeben, wie im uhr.tickt-Beispiel oder in 101.to_s. Manchmal ist es aber nicht so ersichtlich, wie etwa bei den arithmetischen Methoden. Wie sich herausstellt, ist 5+5 eine Abkürzung für 5.+5. Zum Beispiel:

puts 'hallo '.+ 'Welt'
puts (10.*9).+ 9
hallo Welt
99

Das sieht nicht schön aus und wir werden es nie wieder so schreiben; doch es ist wichtig zu verstehen, was wirklich passiert. (Mein Computer hat mir hierbei eine Warnung gegeben: warning: parenthesize argument(s) for future version (=Argumente klammern für zukünftige Versionen). Er hat das Programm problemlos ausgeführt, jedoch hat es mir mitgeteilt, dass es Probleme hat, herauszufinden, was ich meine und ich solle doch in Zukunft mehr Klammern verwenden.) Damit ist auch leichter zu verstehen, warum wir 'Schwein'*5 machen können, nicht aber 5*'Schwein': 'Schein'*5 sagt, dass 'Schwein' die Multiplikation ausführen soll, doch 5*'Schwein' sagt, dass 5 multiplizieren soll. 'Schwein' weiß, wie es 5 Kopien von sich selbst herstellt und addiert; währen 5 Probleme hat, 'Schwein' Kopien von sich selbst herzustellen und zu addieren.

Und natürlich müssen wir puts und gets erklären. Wo sind ihre Objekte? Im Deutschen kann man manchmal das Nomen weglassen, zum Beispiel, wenn ein Bösewicht schreit "Stirb!", so ist das implizierte Nomen die Person, die er anschreit. Wenn ich in Ruby schreibe puts 'sein oder nicht sein', so ist dies eine Abkürzung für self.puts 'sein oder nicht sein'. Aber was ist self? Das ist eine besondere Variable, die auf das Objekt zeigt, in dem du gerade drin bist. Wir wissen noch nicht, wie man in einem Objekt drin ist, aber wir werden es noch herausfinden und bis dann befinden wir uns einfach mal in einem großen Objekt... in dem ganzen Programm! Und glücklicherweise hält das Programm ein paar Methoden für uns bereit, wie etwa puts und gets. Schau mal:

unglaublichLangerVariablenNameDerNurAuf3Zeigt =3
puts unglaublichLangerVariablenNameDerNurAuf3Zeigt
self.puts unglaublichLangerVariablenNameDerNurAuf3Zeigt
3
3

Wenn du das jetzt noch nicht ganz verstanden hast, so ist das auch o.k. Wichtig ist die Erkenntnis, dass jede Methode von einem Objekt ausgeführt wird, auch wenn kein Punkt davor steht. Wenn das klar ist, kann es weitergehen!

Lustige String-Methoden

Lass uns neue lustige String-Methoden kennen lernen. Du musst sie dir nicht alle merken, du kannst ja auf dieser Seite nachschlagen, wenn du sie brauchst. Ich möchte dir nur ein wenig von dem zeigen, was Strings tun können. Tatsächlich kann ich mir selber nicht einmal die Hälfte aller verfügbaren String-Methoden merken, aber auch das ist in Ordnung, denn es gibt tolle Seiten im Internet, die alle Methoden listen und erklären. (Am Ende des Tutorials zeige ich dir, wo du sie findest.) Ehrlich gesagt, will ich mir auch nicht alle Methoden merken, das wäre ja wie alle Wörter im Wörterbuch zu kennen. Ich kann auch sehr gut Deutsch sprechen ohne jedes Wort zu kennen... und das ist ja auch der Grund dafür, dass es Wörterbücher überhaupt gibt: dass man nicht alle Wörter kennen muss! Unsere erste String-Methode dreht den String um:

var1 ='stop'
var2 ='kannst du den Satz rückwärts lesen?'
puts var1.reverse
puts v2.reverse
puts var1
puts var2
pots
?nesel sträwkcür ztaS ned ud tsnnak
stop
kannst du den Satz rückwärts lesen?

Du siehst: reverse dreht nicht den Original-String um; es macht nur eine neue Rückwärts-Version von ihm. Darum ist var1 immer noch 'stop', auch nach dem Aufruf von reverse. Eine andere String-Methode ist length, die uns die Anzahl der Zeichen im String mitteilt, inklusive Leerzeichen:

puts 'wie heißt du?'
name = gets.chomp
puts 'In deinem Namen sind '+name.lengths+' Zeichen, '+name+'!'
wie heißt du?
Christopher David Pine
#<TypeError: can't convert Fixnum into String>

Oha! Da lief etwas schief, und es scheint, dass es irgendwo nach der Zeile name = gets.chomp passiert sein muss... siehst du das Problem? Schau mal, ob du es selbst beheben kannst! Das Problem liegt bei length: es gibt uns eine Zahl, doch wir wollen einen String. Ganz leicht: to_s anfügen (und Daumen drücken):

puts 'wie heißt du?'
name = gets.chomp
puts 'In deinem Namen sind '+name.length.to_s+' Zeichen, '+name+'!'
wie heißt du?
Christopher David Pine
In deinem Namen sind 22 Zeichen, Christopher David Pine!

Huch, das ist mir neu... Hinweis: das ist die Anzahl an Zeichen in meinem Namen, nicht die Anzahl an Buchstaben (zähl' sie!). Ich schätze mal, du kannst selber ein Programm schreiben, das einzeln nach dem ersten und zweiten Vornamen, sowie dem Nachnamen fragt, die Längen zusammenzählt... hey, probier' das doch aus! Mach nur, ich warte hier auf dich.

Hast du's geschafft? Gut. Hübsches kleines Programm, nicht wahr? Nach ein paar weiteren Kapiteln wird du fasziniert sein, was du alles kannst!

Es gibt auch ein paar String-Methoden, die Klein- und Großbuchstaben im String ändern können. upcase wandelt jeden Kleinbuchstaben in einen großen um und lowcase wandelt große Buchstaben in kleine. swapcase wandelt kleine in große und gleichzeitig große in kleine Buchstaben um. Und capitalize funktioniert wie downcase, doch das erste Zeichen wird in einen Großbuchstaben verwandelt (wenn es ein Buchstabe ist).

letters = 'aAbBcCdDeE'
puts letters.upcase
puts letters.downcase
puts letters.swapcase
puts letters.capitalize
puts ' a'.capitalize
puts letters
AABBCCDDEE
aabbccddee
AaBbCcDdEe
Aabbccddee
 a
aAbBcCdDeE

Nette Standard-Aufgaben. Wie man aus der puts ' a'.capitalize Zeile ersehen kann, wird das erste Zeichen in einen Großbuchstaben verwandelt, nicht der erste Buchstabe. Ebenso ist zu bemerken, dass die Variable letters nicht verändert wird. Dieses Verständnis ist wichtig. Es gibt Methoden, die das zugehörige Objekt verändern können, aber die werden wir erst später kennenlernen.

Die letzten lustigen Methoden, die wir uns ansehen, übernehmen die Formatierung von Text. Die erste, center, fügt Leerzeichen an den Anfang und das Ende des Strings, um ihn zentriert darzustellen. Ebenso wie wir bisher puts mitteilen mussten, was denn gedruckt werden soll oder +, was addiert werden soll, müssen wir dieser Methode sagen, auf welche Breite der String zentriert werden soll. Wenn wir also ein Lied zentriert haben wollen, würde ich es so machen:

breite = 50
puts('Hast Du etwas Zeit für mich?'.center(breite))
puts('Dann singe ich ein Lied für Dich'.center(breite))
puts('Von 99 Luftballons'.center(breite))
puts('Auf ihrem Weg zum Horizont'.center(breite))
puts('Denkst Du vielleicht grad\' an mich'.center(breite))
puts('Dann singe ich ein Lied für Dich'.center(breite))
puts('Von 99 Luftballons'.center(breite))
puts('Und dass sowas von sowas kommt'.center(breite))
           Hast Du etwas Zeit für mich?
        Dann singe ich ein Lied für Dich
                Von 99 Luftballons
            Auf ihrem Weg zum Horizont
        Denkst Du vielleicht grad' an mich
        Dann singe ich ein Lied für Dich
                Von 99 Luftballons
          Und dass sowas von sowas kommt

Ich glaube, es wird deutlich, wie die center-Methode funktioniert: durch die Variable breite wird die Gesamtbreite bestimmt, die in jeder Zeile verwendet wird. Dass ich diesen Wert in einer Variablen abgespeichert habe, liegt an meiner Faulheit... und Faulheit ist nicht unbedingt eine schlechte Sache in der Programmierung. Angenommen, ich beschließe später, dass ich das Lied breiter formatiert haben möchte, so muss ich nur eine Variable ändern, anstatt in jeder Zeile des Liedes den Wert zu ändern. Bei einem langen Text, kann mir dies viel Zeit ersparen. In der Programmierung ist diese Art der Faulheit eine echte Tugend!

Es fällt bei dem Text auf, dass nicht jeder Zeile exakt zentriert ist und ein Textverarbeitungsprogramm dies wohl schöner gemacht hätte. Wenn du wirklich perfekte Zentrierung (und vielleicht eine hübschere Schriftart) möchtest, dann musst du eine Textverarbeitung verwenden. Ruby ist ein tolles Werkzeug, aber kein Werkzeug ist perfekt für jeden Job.

Zwei weitere Methoden zur Formatierung von Strings heißem ljust und rjust, was für left justify (=Linksausrichtung) und right justify (=Rechtsausrichtung) steht. Sie verhalten sich ähnlich wie center, doch füllen sie den String rechts oder links mit Leerzeichen auf. Schauen wir uns die drei im Vergleich an:

breite = 40
str = '--> text <--'
puts str.ljust(breite)
puts str.rjust(breite)
puts str.center(breite)
puts str.ljust(breite/2) + str.rjust(breite/2)
--> text <--
                            --> text <--
              --> text <--
--> text <--                --> text <--

Selber ausprobieren

  • Schreib ein Programm chef.rb. Es fragt erst unhöflich, was du willst. Es wiederholt deine Antwort in Großbuchstaben und feuert dich im Anschluss, z.B.
    Was wollen Sie?
    eine Gehaltserhöhung
    Was soll das heißen: "EINE GEHALTSERHöHUNG"??? - Sie sind gefeuert!!!
    
  • Schreib ein Inhaltsverzeichnis (für ein Ruby-Tutorial ;-), das den Inhalt folgendermaßen formatiert:
      Inhaltsverzeichnis
    Kapitel 1: Zahlen   Seite 1
    Kapitel 2: Buchstaben Seite 3
    Kapitel 3: Variablen  Seite 7
    

Mathematik

(Dieser Abschnitt ist optional. Es wird von einem gewissen mathematischen Grundwissen ausgegangen. Wenn es dich nicht interessiert, kannst du gleich mit dem nächsten Kapitel weitermachen. Häufig ist aber ein kurzer Blick auf die Zufallszahlen hilfreich.) Es gibt nicht annähernd so viele mathematische Methoden wie String-Methoden (und dennoch kenne ich sie nicht alle auswendig). Hier betrachten wir erst ein paar arithmetische Methoden, einen Zufallszahlengenerator und das Mathematik-Objekt Math mit seinen trigonometrischen und transzendenten Methoden.

Mehr Arithmetik

Die anderen beiden arithmetischen Methoden sind ** (Exponent) und % ( Modulo). Wenn man in Ruby �fünf zum Quadrat� sagen will, schreibt man 5**2. Man kann für den Exponenten auch Floats verwenden, die Quadratwurzel von 5 ist dann also: 5**0.5. Der Modulo-Operator gibt ins den Rest einer Division, wenn man zum Beispiel 7 duch 3 teilt, lautet das Ergebnis 2 Rest 1. Schauen wir uns das im Programm an:

puts 5**2
puts 5**0.5
puts 7/3
puts 7%3
puts 365%7
25
2.23606797749979
2
1
1

In der letzten Zeile sieht man, dass ein Nicht-Schaltjahr eine gewisse Anzahl von Wochen hat, plus einen Tag. Wenn Dein Geburtstag in einem Jahr an einem Dienstag ist, fällt er im nächsten Jahr auf einen Mittwoch. Man kann den Operator auch mit Floats verwenden, du kannst ja mal damit rumspielen... Eine letzte Methode möchte ich noch ansprechen, bevor wir uns Zufallszahlen zuwenden: abs. Sie berechnet uns den Absolutbetrag einer Zahl:

puts ((5-2).abs)
puts ((2-5).abs)
3
3

Zufallszahlen

Ruby hat einen ganz netten Zufallszahlengenerator. Die Methode, um eine Zufallszahl zu bekommen, heißt rand. Ruft man rand ohne Parameter auf, erhält man eine Zahl größer oder gleich 0.0 und kleiner 1.0. Wenn man rand einen Integer als Argument gibt (etwa 5), so erhält man eine Integer-Zufallszahl größer gleich 0 und kleiner 5, also eine der fünf Zahlen 0, ... 4.

Schauen wir uns rand in Aktion an:

puts rand
puts rand
puts rand
puts rand(100)
puts rand(100)
puts rand(100)
puts rand(1)
puts rand(1)
puts rand(1)
puts rand(9999999999999999999999999999999999999999999999999999)
puts 'Der Wetterbericht meldet'+rand(101).to_s+'% Regen,'
puts 'aber dem kann man eh nicht trauen!'
0.866769322351658
0.155609260113273
0.208355946789083
61
46
92
0
0
0
8934789238746744656765123211649149621976283674627386
Der Wetterbericht meldet 45% Regen,
aber dem kann man eh nicht trauen!

Beachte, dass man mit rand(101) eine Zahl zwischen 0 und 100 erhält, rand(1) immer 0. Unverständnis über die möglichen Rückgabewerte ist der größte Fehler, den man mit rand machen kann. Ich hatte mal einen CD-Player, der bei zufälliger Wiedergabe alle Lieder anspielte, außer dem letzten... (ich frage mich, was passiert wäre, wenn ich eine CD mit nur einem Lied eingelegt hätte?)

Manchmal möchte man dieselben Zufallszahlen in derselben Reihenfolge bei zwei verschiedenen Abläufen des Programms bekommen. (Zum Beispiel habe ich mal Zufallszahlen verwendet, um eine zufällige Welt in einem Computerspiel zu verwenden. Wenn ich eine Welt fand, die mir gefiel, wollte ich sie speichern, oder einem Freund schicken.) Dafür muss man einen so genannten seed setzen, was man mit srand macht.

srand 1776
puts rand(100)
puts rand(100)
puts rand(100)
srand 1776
puts rand(100)
puts rand(100)
puts rand(100)
24
35
36
24
35
36

Verwendet man srand, wiederholt es die Zufallszahlen. Willst du wieder ganz andere Zahlen erhalten (wie wenn du nie srand verwendet hast), verwende srand(0). Damit wird ein neuer komischer Seed verwendet, der sich unter anderem aus der Zeit im Computer in Millisekunden berechnet.

Das Objekt Math

Schließlich bertrachten wir das Math Objekt und fangen sofort mit Beispielen an:

puts (Math::PI)
puts (Math::E)
puts (Math.cos(Math::PI/3))
puts (Math.tan(Math::PI/4))
puts (Math.log(Math::E**2))
puts ((1+Math.sqrt(5))/2)
3.14159265358979
2.71828182845905
0.5
1.0
2.0
1.61803398874989

Dir fällt sicher sofort die ::-Notation auf. Die Erklärung dieses so genannten Scope-operators liegt außerhalb dieses Tutorials. Es reicht, wenn ich betone, dass Math::PI genau das ist, was du erwartest: die Zahl PI.

Wie du siehst, bietet Math alle Möglichkeiten eines anständigen Taschenrechners, und mit den Floats kann man ziemlich genau rechnen.

Nun, fließen wir zum nächsten Thema...