|
|||
if
-Anweisungen und andere Einzelheiten, ist es
schwierig, Beispiele für Klassen zu schreiben. Während unserer
Von-oben-nach-unten-Beschreibung liefen wir dauernd auf diese
grundlegenden Sprachdetails auf, die wir erst behandeln mussten,
wenn unser Beispiel verständlich sein sollte.
Daher entwickelten wir einen anderen großen Plan (man nennt uns nicht
umsonst pragmatisch). Wir beschreiben Ruby immer noch von oben nach unten. Aber vorher fügen wir ein kurzes Kapitel ein, das all die
allgemeinen Sprachmerkmale und das spezielle Vokabular von Ruby
beschreibt, eine Art Kurzeinführung, um auf den Rest des Buches
vorzubereiten.
Song
wird man verschieden Instanzen von populären Hits brauchen, wie etwa ``Ruby Tuesday'',
``Enveloped in Python'', ``String of Pearls'', ``Small talk'' und so weiter. Das Wort
Objekt wird auch statt Klasseninstanz benutzt (und weil wir schreibfaul sind, werden wir es wahrscheinlich oft benutzen).
In Ruby werden diese Objekte erzeugt, indem man einen Konstruktor
aufruft, eine spezielle mit der Klasse verbundene Methode. Der
Standardkonstruktor heißt new
.
song1 = Song.new("Ruby Tuesday") song2 = Song.new("Enveloped in Python") # and so on |
"gin joint".length |
» | 9 |
"Rick".index("c") |
» | 2 |
-1942.abs |
» | 1942 |
sam.play(aSong) |
» | "duh dum, da dum de dum ..." |
number = Math.abs(number) // Java code |
abs
zu
einem Zahl-Objekt und lässt dieses dann die Arbeit machen.
number = number.abs |
strlen(name)
, in Ruby dagegen name.length
und
so weiter. Teilweise ist es dies, wenn wir sagen, dass Ruby von Grund
auf eine OO-Sprache ist.
def sayGoodnight(name) result = "Goodnight, " + name return result end # Time for bed... puts sayGoodnight("John-Boy") puts sayGoodnight("Mary-Ellen") |
#
an und gehen bis ans Ende der Zeile. Das Layout
des Codes ist Sache des Autors, Einrückungen sind nicht signifikant.
Methoden werden mit dem Schlüsselwort def
definiert, dann kommt
der Methoden-Name (in diesem Fall ``sayGoodnight
'') und dann
die Parameter der Methode in Klammern. Ruby benutzt keine geschweiften
Klammern, um die Anweisungen und Definitionen zu begrenzen. Stattdessen
beendet man einfach den Methodenrumpf mit dem abschließenden Schlüsselwort end
.
Unser Methodenrumpf ist ziemlich einfach. In der ersten Zeile hängt man den
Parameter name
an den literalen String ``Goodnight,result
nicht haben deklarieren müssen, sie wurde geboren, als wir
auf sie zugriffen.
Nachdem wir die Methode definiert haben, rufen wir sie zweimal auf. In beiden
Fällen geben wir das Ergebnis weiter an die Methode puts
, die
einfach ihr Argument gefolgt von einem Newline ausgibt.
Goodnight, John-Boy Goodnight, Mary-Ellen |
puts sayGoodnight("John-Boy")
'' enthält zwei Methodenaufrufe,
einen für sayGoodnight
und einen für puts
. Warum sind
jetzt bei dem einen Aufruf die Argumente in Klammern und bei dem andern nicht? In diesem
Fall ist das einfach nur Geschmacksache. die folgenden Zeilen sind alle äquivalent.
puts sayGoodnight "John-Boy" puts sayGoodnight("John-Boy") puts(sayGoodnight "John-Boy") puts(sayGoodnight("John-Boy")) |
\n
'' zum Erzeugen einer neuen Zeile.
puts "And Goodnight,\nGrandma" |
And Goodnight, Grandma |
#{
expression}
ersetzt durch den Wert von
expression. Wir können das benutzen, um unsere frühere Methode
anders zu schreiben.
def sayGoodnight(name) result = "Goodnight, #{name}" return result end |
name
raus und setzt das in den String ein.
Die Ausdrücke im #{...}
-Konstrukt dürfen so komplex sein wie nur denkbar sein.
Man braucht die geschweiften Klammern nicht unbedingt, wenn der
Ausdruck eine globale, Instanz- oder Klassen-Variable ist. Mehr
Informationen über Strings und andere Standard-Typen gibts in
Kapitel 5 ab Seite 49.
Zum Schluss können wir diese Methode noch weiter vereinfachen. Der Wert,
den eine Methode zurückgibt, ist der Wert des letzten berechneten Ausdrucks,
also können wir das return
auch noch weglassen.
def sayGoodnight(name) "Goodnight, #{name}" end |
Beispiel-Variablen und -Klassen-Namen
|
a = [ 1, 'cat', 3.14 ] # Array mit drei Elementen |
||
# Zugriff auf erstes Element |
||
a[0] |
» | 1 |
# das dritte Element setzen |
||
a[2] = nil |
||
# das komplette Array |
||
a |
» | [1, "cat", nil] |
Array.new
.
empty1 = [] empty2 = Array.new |
%w
macht da genau das, was wir brauchen
a = %w{ ant bee cat dog elk } |
||
a[0] |
» | "ant" |
a[3] |
» | "dog" |
instSection = { 'cello' => 'string', 'clarinet' => 'woodwind', 'drum' => 'percussion', 'oboe' => 'woodwind', 'trumpet' => 'brass', 'violin' => 'string' } |
instSection['oboe'] |
» | "woodwind" |
instSection['cello'] |
» | "string" |
instSection['bassoon'] |
» | nil |
nil
zurück, wenn man über einen Schlüssel zugreift, der nicht existiert.
Normalerweise reicht das auch, weil in Abfrageanweisungen nil
dasselbe wie false
bedeutet. Man kann diesen Defaultwert
aber auch ändern. Wenn man etwa einen Hash benutzt, um zu zählen, wie
oft ein Schlüssel vorkommt, so ist es sinnvoll, diesen Defaultwert auf Null zu
setzen. Dies macht man einfach, indem man beim Erzeugen eines neuen,
leeren Hashs diesen Defaultwert mit angibt
histogram = Hash.new(0) |
||
histogram['key1'] |
» | 0 |
histogram['key1'] = histogram['key1'] + 1 |
||
histogram['key1'] |
» | 1 |
if
-Abfragen
und while
-Schleifen. Java-, C- und Perl-Programmierer werden
überrascht sein, dass die üblichen Klammern um den Rumpf dieser
Anweisungen fehlt. Statdessen benutzt Ruby das Schlüsselwort end
,
um das Ende einer solchen Anweisung anzuzeigen.
if count > 10 puts "Try again" elsif tries == 3 puts "You lose" else puts "Enter a number" end |
while
-Anweisungen mit end
abgeschlossen.
while weight < 100 and numPallets <= 30 pallet = nextPallet() weight += pallet.weight numPallets += 1 end |
if
- oder while
-Anweisung nur aus einem
Ausdruck besteht. Man schreibt einfach diesen Ausdruck, gefolgt von
if
oder while
und der Bedingung.
Als Beispiel hier eine einfache if
-Anweisung.
if radiation > 3000 puts "Danger, Will Robinson" end |
puts "Danger, Will Robinson" if radiation > 3000 |
while
-Schleife
while square < 1000 square = square*square end |
square = square*square while square < 1000 |
/Perl|Python/ |
|
''). Man kann Klammern
innerhalb des Musters benutzen, gerade wie in einem arythmetischen
Ausdruck. Dieses Muster könnte also auch so aussehen.
/P(erl|ython)/ |
/ab+c/
passt auf einen String mit einem ``a'' gefolgt von
einem oder mehreren ``b''s, gefolgt von einem ``c''. Wenn man das Plus
durch ein Sternchen ersetzt, so passt /ab*c/
auf ein ``a'',
keinem, einem oder mehreren ``b''s und einem ``c''.
Man kann auch ein Zeichen aus einer Gruppe von Zeichen suchen. Die
üblichen Beispiele sind Zeichen-Klassen wie ``\s
'', was auf ein
Whitespace-Zeichen passt (Leertaste, Tabulator, Newline und so was).
``\d
'' passt auf Ziffern, ``\w
'' passt auf einen üblichen
Buchstaben und ``.'' passt auf ein einzelnes Zeichen.
Mit all dem zusammen kann man einige nützliche reguläre Ausdrücke
erzeugen.
/\d\d:\d\d:\d\d/ # eine Zeit so wie 12:34:56 /Perl.*Python/ # Perl, kein, ein oder mehrere andere Zeichen, dann Python /Perl\s+Python/ # Perl, ein oder mehrere Whitespaces, dann Python /Ruby (Perl|Python)/ # Ruby, ein Leerzeichen und entweder Perl oder Python |
=~
'' wird benutzt, um einen
String mit einem regulären Ausdruck zu vergleichen. Wenn das Muster in
dem String gefunden wird, so gibt =~
die Startposition zurück, wenn
nicht ein nil
. Damit kann man reguläre Ausdrücke in if
- und
while
-Anweisungen benutzen. Als Beispiel schreibt das folgende
Stück Code eine Meldung, wenn ein String den Text 'Perl' oder 'Python'
enthält.
if line =~ /Perl|Python/ puts "Scripting language mentioned: #{line}" end |
line.sub(/Perl/, 'Ruby') # ersetzt das erste 'Perl' durch 'Ruby' line.gsub(/Python/, 'Ruby') # ersetzt alle 'Python' durch 'Ruby' |
do
...end
.
{ puts "Hello" } # das ist ein Block do # club.enroll(person) # und das hier auch person.socialize # end # |
yield
-Anweisung von Ruby
benutzt. Das folgende Beispiel zeigt dies. Wir definieren eine Methode, die
yield
zweimal benutzt. Dann rufen wir diese Methode auf, wobei wir
den erwünschten Block auf die selbe Zeile dahinter setzen (und hinter eventuell
vorhandene Parameter) (der Übersetzer: zumindest muss der Block in der selben
Zeile anfangen). [Manche Leute stellen sich diese
Verbindung eines Blockes mit einer Methode gern als eine Art von
Parameterübergabe vor. Das kann man machen, es trifft die Sache aber
nicht ganz genau. Man sollte sich den Block und die Methode lieber
als zwei Ko-Routinen vorstellen, die die Programmkontrolle zwischen
sich hin und her schieben.]def callBlock yield yield end callBlock { puts "In the block" } |
In the block In the block |
puts "In the block"
) zweimal
ausgeführt wird, einmal für jedes yield
.
Man kann dem yield
auch Parameter mitgeben, diese werden an den
Block weitergegeben. Innerhalb des Blocks definiert man dann in senkrechten
Strichen (``|'') eine Liste mit Namen, um diese Parameter entgegenzunehmen.
def callBlock yield , end callBlock { |, | ... } |
a = %w( ant bee cat dog elk ) # erzeuge ein Array a.each { |animal| puts animal } # über dessen Inhalt iterieren |
ant bee cat dog elk |
each
-Iterator der
Array
-Klasse aus dem vorigen Beispiel benutzen
können. Der each
-Iterator springt durch jedes Element aus
dem Array und ruft dabei jedes Mal each
mit diesem
Element auf. In Pseudo-Code sieht das etwa so aus:
# Innerhalb der Array-Klasse... def each for each element yield(element) end end |
each
-Methode
iterieren und dabei einen Block mitschicken. Dieser Block wird dann für
jedes Element aufgerufen.
[ 'cat', 'dog', 'horse' ].each do |animal| print animal, " -- " end |
cat -- dog -- horse -- |
5.times { print "*" } 3.upto(6) {|i| print i } ('a'..'e').each {|char| print char } |
*****3456abcde |
each
-Methode
einen Block auf.
puts
schreibt jedes seiner Elemente mit
jeweils einem Newline (neue Zeile). print
macht das auch, aber
ohne Newline. Beide können auf alle möglichen Ausgabe-Objekte schreiben,
aber im Normalfall schreiben sie auf die Konsole.
Eine andere Methode, die wir oft benutzen werden, ist printf
, die
ihre Argumente ausgibt mithilfe eines Format-Strings (gerade wie printf
in C oder Perl).
printf "Number: %5.2f, String: %s", 1.23, "hello" |
Number: 1.23, String: hello |
"Number: %5.2f, String: %s"
der printf
-Methode, dass sie eine Umwandlung vornehmen soll
in eine Fleißkommazahl (mit 5 moglichen Zeichen, 2 nach dem Dezimalpunkt)
und in einen String.
Es gibt viele Wege, Input in dein Programm zu kriegen. Die wahrscheinlich
gebräuchlichste Methode ist die Routine gets
, die die nächste
Zeile aus dem Standard-Input-Kanal (meistens die Tastatur) des Programms holt.
line = gets print line |
gets
-Routine hat einen Seiteneffekt: sie gibt nicht nur die
gerade gelesene Zeile zurück, sie speichert sie auch in der globalen
Variablen $_
. Diese Variable ist insofern speziell, als dass
sie in vielen Fällen als Default-Variable genutzt wird. Wenn man print
ohne Argument aufruft, gibt das den Inhalt von $_
aus. Wenn man
eine if
- oder while
-Anweisung nur mit einem regulären
Ausdruck als Bedingung schreibt, wird dieser Ausdruck gegen $_
getestet. Während einige Puristen dies als frühsteinzeitliche Faustkeilprogrammierung
ansehen, kann diese Abkürzung einem helfen, recht knappe Programme zu schreiben.
Als Beispiel gibt das folgende Programm alle Zeilen des Input-Kanals aus,
die das Wort ``Ruby'' enthalten.
while gets # Zeile auf $_ setzen if /Ruby/ # prüft gegen $_ print # Ausgabe $_ end end |
ARGF.each { |line| print line if line =~ /Ruby/ } |
ARGF
benutzt, das den
vom Programm benutzten Input-Kanal repräsentiert.