• Keine Ergebnisse gefunden

Ruby Grundlagen. Prof. Thomas Smits. Wintersemester 2021/ November 2021

N/A
N/A
Protected

Academic year: 2022

Aktie "Ruby Grundlagen. Prof. Thomas Smits. Wintersemester 2021/ November 2021"

Copied!
219
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Programmierung 3 (PR3) - Ruby Vorlesung - Hochschule Mannheim

Ruby Grundlagen

Prof. Thomas Smits

Wintersemester 2021/2022

9. November 2021

Diese Materialien sind ausschließlich für den persönlichen Gebrauch durch Besucher der Vorlesung Programmierung 3 (PR3) an der Hochschule Mannheim gedacht. Jede andere Nutzung ist ohne vorherige Zustimmung des Autors nicht zulässig.

(2)

Inhaltsverzeichnis

1 Einführung 1

1.1 Was ist Ruby? [5] . . . 1

1.2 Eigenschaften [6] . . . 2

1.3 Interactive Ruby Shell [7] . . . 3

1.4 Schlüsselworte [8] . . . 4

1.5 Klammern in Methoden [9] . . . 4

1.6 Strings [10]. . . 5

1.7 Formatter [11] . . . 6

1.8 Alles is ein Objekt [12] . . . 7

1.9 nil ist ein Objekt [13] . . . 8

1.10 Symbole [14] . . . 9

1.11 Symbol↔String [15] . . . 10

1.12 Ranges [16]. . . 11

1.13 Parallele Zuweisung [18] . . . 12

1.14 Bedingte Zuweisung [20] . . . 13

1.15 Kommentare [21] . . . 14

1.16 Namenskonventionen [22] . . . 14

1.17 Methodennamen [23] . . . 15

1.18 Zeilenorientierung [24] . . . 16

1.19 BEGIN und END-Blöcke [25] . . . 17

2 Kontrollstrukturen 19 2.1 Überblick [27] . . . 19

2.2 if [28] . . . 19

2.3 unless [31] . . . 21

2.4 Was ist true? [32] . . . 21

2.5 Double Bang Operator [33] . . . 22

2.6 Case [34] . . . 23

2.7 Case und Ranges [37] . . . 25

3 Schleifen 27 3.1 Kopfgesteuerten while-Schleife [39] . . . 27

3.2 while als Modifier [40] . . . 28

Seite ii

(3)

Inhaltsverzeichnis Inhaltsverzeichnis

3.3 Fußgesteuerte while-Schleife [41] . . . 28

3.4 until-Schleife [42] . . . 29

3.5 for-Schleife [43] . . . 30

3.6 Kontrolle der Schleife [45] . . . 31

3.7 Scope der Variablen [49] . . . 32

4 Ausdrücke 34 4.1 Alles hat einen Rückgabewert [51]. . . 34

4.2 Operatoren als Methoden [52] . . . 34

4.3 Beispiel: Operatoren implementieren [53] . . . 35

4.4 Betriebssystem-Kommandos [55] . . . 36

4.5 Zuweisungen [56] . . . 37

4.6 Parallele Zuweisung [57] . . . 38

4.7 Splat [58] . . . 39

4.8 Bedingte Ausführung [59] . . . 40

4.9 defined? [60] . . . 41

4.10 Vergleichsoperatoren [61] . . . 42

5 Eingabe 45 5.1 Einlesen von der Konsole [63] . . . 45

5.2 Umwandlung der Daten [65] . . . 46

6 Organisation der Quelltexte 47 6.1 Dateinamen [67] . . . 47

6.2 Datei-Struktur [68] . . . 47

6.3 Dateien laden [70] . . . 48

6.4 Gems [72] . . . 49

7 Unit-Tests 51 7.1 Frameworks [74]. . . 51

7.2 Minitest Assertions [75]. . . 51

7.3 Beispiel: Minitest [76] . . . 52

7.4 Spec-Style [77] . . . 53

8 Ruby-Dokumentation 54 8.1 Werkzeuge zur Dokumentation [79] . . . 54

8.2 ri [80] . . . 54

8.3 Methoden beschreiben [81] . . . 55

8.4 Auszeichnungen [82] . . . 56

8.5 Yard [84] . . . 56

8.6 Yard-Tags [86] . . . 57

(4)

Kapitel 1

Einführung

1.1 Was ist Ruby? [5]

Entwickelt seit 1993 von Yukihiro Matsumoto (Matz) in Japan

Erste Veröffentlichung 1995

⇒genauso alt wie Java

Extrem populär für Webentwicklung wegenRuby on Rails

Yukihiro Matsumoto, Quelle: Wikipedia

Ruby ist als Sprache genauso alt wie Java, hat aber deutlich andere Konzepte und Wurzeln. Yukihiro Matsumoto hat für Ruby die Konzepte der Programmiersprachen Perl, Smalltalk, Eiffel, Ada und Lisp kombiniert und versucht eine Sprache zu entwickeln, die sowohl funktionale als auch imperative Programmierung erlaubt.

I am trying to make Ruby natural, not simple.

Yukihiro Matsumoto

Mit der Entwicklung des WebframeworksRuby on Railswurde Ruby extrem populär und sie ist heute imTIOBE-Indexnoch immer unter den populärsten Programmiersprachen (Stand Januar 2021 auf Platz 15).

Seite 1

(5)

1 Einführung 1.2 Eigenschaften [6]

1.2 Eigenschaften [6]

Ruby hat einige zentrale Eigenschaften, die da sind:

Skriptsprache (⇒kein Compiler)

multiparadigmatisch

objektorientiert (Klassen oder Prototypen)

prozedural

funktional

Metaprogrammierung

aspektorientierte Programmierung

alles ist ein Objekt

dynamische Typisierung

plattformunabhängig

Bei Ruby handelt es sich um eine Skriptsprache, die – anders als Java – keinen Compiler benö- tigt, sondern durch einem Interpreter ausgeführt wird. Der Interpreter ist mit einer Java VM zu vergleichen, allerdings verarbeitet er direkt den Quelltext und keinen Bytecode wie Java.

Eine Besonderheit von Ruby ist, dass die Sprache nicht streng einem Paradigma folgt. Anders als Java, das rein objektorientiert ist oder Lisp, das rein funktional ist, unterstützt Ruby mehrere Programmierparadigmen.

objektorientiert: In Ruby kann man Klassen definieren und Objekte manipulieren. Ruby geht sogar so weit, dass alles in Ruby ein Objekt ist, d. h. es gibt z. B. keine primitiven Datentypen.

prozedural: Man muss in Ruby nicht mit Klassen und Objekten arbeiten, sondern man kann wie in C ausschließlich mit Funktionen und Prozeduren programmieren. Hierdurch kann man Skripte schreiben, die nur ein paar Zeilen enthalten.

funktional: In Ruby sind Funktionen eigenständige Entitäten, die man – wie in der funktiona- len Programmierung üblich – analog zu Objekten manipulieren und verarbeiten kann.

Metaprogrammierung: Ruby erlaubt es, dass Programme sich selbst oder andere Programme verändern können.

aspektorientierte Programmierung: Es ist in Ruby möglich verschiedene Aspekte des Pro- gramms (z. B. Logging und normale Logik) voneinander zu trennen und als sogenannte Aspekte zu behandeln.

Wie bei Skriptsprachen üblich, müssen Variablen in Ruby nicht mit einem Typ deklariert werden, sondern entstehen bei der ersten Benutzung und können zu verschiedenen Zeiten Daten unter- schiedlichen Typs enthalten. Die von den Variablen referenzierten Objekte (alles ist in Ruby ein Objekt) kennen ihren Typ, den Variablen ist dieser aber egal, sie zeigen einfach auf alles.

(6)

1 Einführung 1.3 Interactive Ruby Shell [7]

Variablen in Ruby

a = 5 a = 'Hallo'

Variablen in Java

int a = 5;

String b = "Hallo";

Ruby ist eine plattformunabhängige Sprache, weil Ruby-Programme auf allen Systemen ausgeführt werden können, für die es einen Ruby-Interpreter gibt. Da ein in Java geschriebenen Ruby-Interpreter (JRuby) existiert, kann man Ruby auf allen Plattformen ausführen, für die eine Java-VM verfügbar ist.

1.3 Interactive Ruby Shell [7]

Interactive Ruby Shell(irb)

→Programme direkt interaktiv ausprobieren

REPL= Read Evaluate Print Loop

Start mit dem Kommandoirb

~$ irb

irb( main ):001:0 > puts " Hello World "

Hello World

=> nil

irb(main ):002:0 > 2+2*2

=> 6

irb(main ):003:0 > exit

~$

Mit derInteractive Ruby Shell(irb) kann man Ruby Befehle interaktiv ausprobieren. Jeder einge- gebene Befehl wird sofort evaluiert und der Rückgabewert wird ausgegeben. Anders als in einem

„echten“ Programm muss man beiirbdie Ergebnisse nicht extra mitprintoderputsausgeben.

Will man ein Ruby-Programm ausführen, das als Datei vorliegt, verwendet man anstatt der irb den Ruby-Interpreterruby. Ein beispielhafter Aufruf sieht damit so aus:ruby mein_skript.rb. Unter Unix kann man ein Ruby-Skript direkt als ausführbar markieren (chmod a+x DATEINAME).

Dazu muss die erste Zeile des Skripts den Pfad zum Ruby-Interpreter enthalten, z. B.

#!/usr/bin/ruby puts 2+2

Ruby Grundlagen Seite 3

(7)

1 Einführung 1.4 Schlüsselworte [8]

Den speziellen Ausdruck#!bezeichnet man alsShebang). Er muss in der ersten Zeile des Skriptes stehen, um zu funktionieren.

1.4 Schlüsselworte [8]

__ENCODING__ __FILE__ __LINE__ alias and

begin BEGIN break case class

def defined ? do else elsif

end END ensure false for

if in module next nil

not or redo rescue retry

return self super then true

undef unless until when while

yield

Ruby hat weniger Schlüsselworte als Java, das ca. 50 besitzt.

Java Schlüsselworte

abstract continue for new switch

assert default if package synchronized

boolean do goto private this

break double implements protected throw

byte else import public throws

case enum instanceof return transient

catch extends int short try

char final interface static void

class finally long strictfp volatile

const float native super while

Vieles, das auf den ersten Blick wie ein Schlüsselwort aussieht, z. B. Schleifen mitloop, sind in Wirklichkeit Funktionen, die durch Rubys flexiblen Syntax nicht als solche erscheinen.

1.5 Klammern in Methoden [9]

Klammern bei Methodenaufrufen können weggelassen werden

> "Hallo".size

=> 5

> "Hallo".size()

=> 5

(8)

1 Einführung 1.6 Strings [10]

> "Hallo Thomas".split(' ')

=> ["Hallo", "Thomas"]

> "Hallo Thomas".split ' '

=> ["Hallo", "Thomas"]

Dem Prinzip folgend, dass Ruby möglichst natürlich sein soll, sind z. B. die Klammern bei Methoden- aufrufen optional. Bei der Deklaration von Methoden kann man ebenfalls die Klammern weglassen, wobei die Konvention bei den meisten Ruby-Programmen wie folgt aussieht:

Keine Klammern

Deklaration einer Methode ohne Parameter:def m

Aufruf einer Methode ohne Parameter:m

Mit Klammern

Deklaration einer Methode mit Parametern:def m(a, b)

Aufruf einer Methode mit Parametern:m(1, 2)

1.6 Strings [10]

Begrenzer(Delimiter) für Strings sind'oder"

> puts "Hallo"

Hallo

> puts 'Hallo' Hallo

Variablen können innerhalb von"-Strings über#{VAR}eingesetzt werden

> a = 77

> puts "A ist #{a}"

A ist 77

> puts 'A ist #{a}' A ist #{a}

Anders als in Java, wird in Ruby nicht zwischenStringundCharacterunterschieden, d. h. selbst ein einzelnes Zeichen ist ein String. Trotzdem gibt es zwei verschiedene Arten von String-Begrenzern 'und", die sich darin unterscheiden, wie Variablen undEscape-Sequenzenbehandelt werden.

Strings, die mit'begrenzt werden:

Variablen#{VAR}und andere Ruby-Ausdrücke#{AUSDRUCK}werdennichtersetzt

Ruby Grundlagen Seite 5

(9)

1 Einführung 1.7 Formatter [11]

Escape-Sequenzen (\) werden nicht interpretiert, außer\' Strings, die mit"begrenzt werden:

Variablen#{VAR}und andere Ruby-Ausdrücke#{AUSDRUCK}werden durch ihren Wert ersetzt

Es gibt eine ganze Reihe von Escape-Sequenzen, die interpretiert werden, z. B.

\": Das Anführungszeichen selbst

\n: newline→neue Zeile

\t: tab→Tabulator

\b: backspace→vorheriges Zeichen löschen

. . .

Ein weiterer Vorteil der beiden unterschiedlichen String-Begrenzer ist, dass man den jeweils anderen problemlos in den String einschließen kann.

puts '"Oh, je" sagte die Mutter' puts "Lukas' Idee war grandios"

1.7 Formatter [11]

Ähnlich zuSystem.out.printfin Java:

> puts "Der Preis für %s ist %.2f EUR" % [ "Kuchen", 42.2322222 ]

> sprintf("Der Preis für %s ist %.2f EUR", "Kuchen", 42.2322222)

Der Preis für Kuchen ist 42.23 EUR Der Preis für Kuchen ist 42.23 EUR

Benannte Platzhalter

puts "The %{food} is a %{attribute}" % { food: 'cake', attribute: 'lie' }

The cake is a lie

Die String-Formatierung mit demFormatterfunktioniert analog zur Java-MethodeString.format und verwendet dieselben Platzhalter und Möglichkeiten zur Längenangabe. Sowohl Java als auch

(10)

1 Einführung 1.8 Alles is ein Objekt [12]

Ruby übernehmen diese aus der Funktionsprintfder C-Standardbibliothek. Die Dokumentation zur Funktionsweise der Platzhalter findet sich bei Ruby in der Klasse Kernel in der Methode sprintf.

Das%zwischen dem String und den Daten, die angezeigt werden sollen ist ein überladener Modulo- Operator (%).

Beispiele für Formatierungen

# `+' and space flag specifies the sign of non-negative numbers.

sprintf("%d", 123) #=> "123"

sprintf("%+d", 123) #=> "+123"

sprintf("% d", 123) #=> " 123"

# padding is done by spaces, width=20

# 0 or radix-1. <--->

sprintf("%20d", 123) #=> " 123"

sprintf("%+20d", 123) #=> " +123"

sprintf("%020d", 123) #=> "00000000000000000123"

sprintf("%+020d", 123) #=> "+0000000000000000123"

sprintf("% 020d", 123) #=> " 0000000000000000123"

# precision for `f' is number of

# digits after the decimal point <--->

sprintf("%20.8f", 1234.56789) #=> " 1234.56789000"

# precision for `s' is

# maximum number of characters <--->

sprintf("%20.8s", "string test") #=> " string t"

sprintf("%d %04x", 123, 123) #=> "123 007b"

sprintf("%08b '%4s'", 123, 123) #=> "01111011 ' 123'"

sprintf("%1$*2$s %2$d %1$s", "hello", 8) #=> " hello 8 hello"

sprintf("%1$*2$s %2$d", "hello", -8) #=> "hello -8"

sprintf("%+g:% g:%-g", 1.23, 1.23, 1.23) #=> "+1.23: 1.23:1.23"

sprintf("%u", -123) #=> "-123"

1.8 Alles is ein Objekt [12]

Alles in Ruby ist ein Objekt

Keine primitiven Datentypen

> -2.abs

=> 2

> -2.positive?

=> false

Ruby Grundlagen Seite 7

(11)

1 Einführung 1.9 nil ist ein Objekt [13]

> 2.positive?

=> true

Anders als Java, unterscheidet Ruby nicht zwischen primitiven Datentypen und Objekten. (Fast) alles in Ruby ist ein Objekt, Ganzzahlen, Fließkommazahlen etc. Als Konsequenz hieraus, kann man auf allen „Daten“ in Ruby Methoden aufrufen.

Die einzige Ausnahme in Ruby sind Blöcke, Methoden und Funktionen, die keine Objekte sind. Sie lassen sich aber überProcsundLambdasjederzeit in Objekte umwandeln, sodass im Endeffekt alles als Objekt manipuliert und betrachtet werden kann.

Block als Objekt

doppler = Proc.new { |a| 2*a } doppler.call(5) # => 10

Funktion/Methode als Objekt

# Funktion (kein Objekt) def quadrierer(a)

a*a end

# Objekt zur Funktion beschaffen m = method(:quadrierer)

m.call(5) # => 25

1.9 nil ist ein Objekt [13]

Java:null⇒kein Objekt, sondern Referenz ins Nichts

Ruby:nil⇒ein Objekt vonNilClass

Java-Beispiel

String a = null;

if (a != null) {

System.out.println(a.length());

}

Ruby-Beispiel

a = nil

if !a.nil? then puts a.size end

(12)

1 Einführung 1.10 Symbole [14]

Im Gegensatz zu Java muss mannilnicht gesondert behandeln, sondern kann es wie jedes andere Objekt verarbeiten. Eine Besonderheit vonnilist, dass es automatisch in Kontrollstrukturen als falseevaluiert wird. Damit kann man sich den Test mitnil?so gut wie immer sparen, da die Variable selbst ausgewertet werden kann und bei einem Wert vonnilalsfalsegilt.

a = nil if a then

puts a.size end

1.10 Symbole [14]

Symbole(Symbols)

Konstante Namen

Müssen nicht vordeklariert werden

Singletons

def walk(direction) if direction == :north

# ...

end end

walk(:east) walk(:west)

Symbole sind eine Besonderheit von Ruby und führen oft zu Irritationen. Symbole erfüllen ungefähr die Aufgabe, die Enumerationen in Java haben: Die Definition von festen, unveränderlichen Werten, die für die Übergabe an Methoden benutzt werden können. Außerdem werden Symbole überall sonst eingesetzt, wo man konstante Werte benötigt, z. B. um Methoden zu benennen, um Keys in Hashes (⇔Mapin Java) zu definieren etc.

Enumeration in Java

public enum Planet {

MERKUR, VENUS, ERDE, MARS, JUPITER,SATURN, URANUS, NEPTUN;

// PLUTO;

}

public class SpaceTravel {

private static final Planet HOME_PLANET = Planet.ERDE;

Ruby Grundlagen Seite 9

(13)

1 Einführung 1.11 SymbolString [15]

public void travel(Planet zielPlanet) { if (zielPlanet == HOME_PLANET) {

return; // wir sind doch schon da }

switch (zielPlanet) {

case JUPITER: // langer Flug break;

case MARS: // kurzer Flug break;

// ...

} } }

Analoges Programm in Ruby

HOME = :erde def travel(ziel)

return 'schon da' if ziel == HOME case ziel

when :jupiter 'langer Flug' when :mars

'kurzer flug'

# ...

endend

Strings sind in Ruby veränderbar, Symbole nicht. Deswegen können sie alsSingletonsrealisiert werden und lösen das Problem, dass der Schlüssel in einer Hashtabelle (Hash in Ruby) nachträglich nicht verändert werden darf. Generell sollte man immer dann Symbole bevorzugen, wenn man nicht den Inhalt des Strings benötigt, sondern er nur als Platzhalter für einen möglichen Wert dient.

1.11 SymbolString [15]

id2name,to_s: Symbol→String

intern,to_sym: String→Symbol :my_sym.id2name # => "my_sym"

(14)

1 Einführung 1.12 Ranges [16]

:my_sym.to_s # => "my_sym"

"string_sym".intern # => :string_sym

"string_sym".to_sym # => :string_sym

Häufig möchte man aus einem String ein Symbol erzeugen oder umgekehrt ein Symbol in einen String umwandeln. Hierzu dienen die Methodento_saufSymbolbzw.to_symaufString.

1.12 Ranges [16]

Rangessind ein eigener Datentyp in Ruby

Beschreiben einen Bereich von Werten (Intervall mit Beginn und Ende)

a..b: Vonabisbinklusiveb

a...b: Vonabisbexklusiveb print (7..2).to_a # => []

print (1..5).to_a # => [1, 2, 3, 4, 5]

print (1...5).to_a # => [1, 2, 3, 4]

print (-5..-1).to_a # => [-5, -4, -3, -2, -1]

print ('a'..'c').to_a # => ["a", "b", "c"]

print ('a'...'c').to_a # => ["a", "b"]

Ein weiterer Datentyp, der relativ spezifisch für Ruby ist, sind dieRanges. Ein Range beschreibt einenWertebereich, der über sein Beginn und Ende angegeben werden kann. Der verwendete Datentyp muss eine natürliche Ordnung haben, damit man feststellen kann, ob ein Wert innerhalb oder außerhalb des Ranges liegt, z. B. Integer, Float, String etc. Eigene Datentypen müssen die

<=>-Methode implementieren (analog zucompareToin Java), um in einem Range verwendet werden zu können.

Wenn der Datentyp zusätzlich noch eine endliche Anzahl von Werten zwischen den Grenzen hat, kann man sich durch dieto_a-Methode alle Elemente als Array ausgeben lassen.Achtung:Dies kann zu einem erheblichen Speicherverbrauch führen.

Der Vorteil von Ranges ist, dass sie

speichersparend sind: Anstatt alle Werte zu speichern, muss man nur die Grenzen ablegen;

außerdem kann man mitinclude?leicht prüfen, ob ein Wert im Range liegt.

Ranges werden später noch ausgiebiger behandelt.

Beispiel: Ranges

print ('aa'..'bb').to_a

Ruby Grundlagen Seite 11

(15)

1 Einführung 1.13 Parallele Zuweisung [18]

Ausgabe

[" aa", "ab", "ac", "ad", "ae", "af", "ag", "ah", "ai", "aj", "ak", "al",

"am", "an", "ao", "ap", "aq", "ar", "as", "at", "au", "av", "aw", "ax",

"ay", "az", "ba", "bb "]

Während der Range'aa'..'bb'nur den Speicher für seine beiden Grenzen verbraucht, wird bei der Umwandlung in einen String mitto_afür jedes Element zwischen diesen beiden Grenzen entsprechend Hauptspeicher benötigt. Aus diesem Grund sollte man Ranges nur dann in Arrays umwandeln, wenn dies auch wirklich nötig ist.

1.13 Parallele Zuweisung [18]

Parallele Zuweisung(parallel assignment)erlaubt in einer Zuweisung mehrere Werte zuzuweisen

a, b = 7, 9 print a # => 7 print b # => 9

a, b, c = 7, 9 print a # => 7 print b # => 9 print c # => nil

a, b = 7, 9, 11 print a # => 7 print b # => 9

Die Parallele Zuweisung ohne Methodenaufruf wird von vielen Ruby-Programmierern als un- übersichtlich abgelehnt. Auch das Code-Qualitätswerkzeug Rubocop bemängelt sie als schlechten Stil.

Kann auch bei Methoden eingesetzt werden, um mehrere Werte zurückzugeben

def pair

return 'a', 42 end

x, y = pair

(16)

1 Einführung 1.14 Bedingte Zuweisung [20]

print x # => 'a' print y # => 42

Der Hauptanwendungszweck von parallelen Zuweisungen ist die Rückgabe von mehreren Werten aus einer Methode. Im Gegensatz zu Java, wo man eine eigene Klasse verwenden muss, um mehr als einen Wert zurückzugeben oder mitObject[]tricksen muss, kann eine Methode in Ruby beliebig viele Werte zurückgeben.

Das oben dargestellte Beispiel sähe in Java wie folgt aus:

public class ReturnDemo { public static class Pair {

public String x;

public int y;

public Pair(String x, int y) { this.x = x;

this.y = y;

} }

public static Pair pair() { return new Pair("a", 42);

}

public static void main(String[] args) { Pair p = pair();

System.out.println(p.x);

System.out.println(p.y);

} }

1.14 Bedingte Zuweisung [20]

||=→Kombination aus||und=-Operator

||=führt nur dann eine Zuweisung durch, wenn die linke Seitenilist

Mit bedingter Zuweisung

a = [ 'A' ] a[0] ||= 'X' a[1] ||= 'Y' print a # => A, Y

Ruby Grundlagen Seite 13

(17)

1 Einführung 1.15 Kommentare [21]

Ohne bedingte Zuweisung

a = [ 'A' ]

a[0] = 'X' if a[0].nil?

a[1] = 'Y' if a[1].nil?

print a # => A, Y

Ruby unterstützt mit derBedingten Zuweisungeine Abkürzung für die Zuweisung von Variablen abhängig davon, ob sienilsind oder nicht. Sie ist im Prinzip eine Kurzform des||-Operators, der in Ruby auf beliebige Objekte angewandt werden kann und dann folgende Logik besitzt:

Der Ausdrucka || bhat den Wert

vonawennanichtnilist

vonbwennanilist

Somit ist der Ausdrucka ||= bnur eine Kurzform füra = a || b. Ohne den||-Operator könnte man auch schreiben:a.nil? ? b : a.

1.15 Kommentare [21]

Zeilenkommentaremit#

Blockkommentarezwischen=beginund=end

# Kommentar

if a > 0 then a = 7 end # Zeilenkommentar

=begin

Ein mehrzeiliger Kommentar, der auch # enthalten darf

=end

Wie bei den allermeisten Skriptsprachen wird in Ruby#als Zeichen für Zeilenkommentare ver- wendet. Es entspricht damit dem//in Java und C++. Zusätzlich kann man mit=beginund=end Blockkommentare markieren (entspricht/*und*/in Java, C und C++).

1.16 Namenskonventionen [22]

Lokale Variablen:name,fisch_kopf,_mit_underscore

Methodennamen:add,enhance_flavor

Instanzvariablen:@name,@last_call

Klassenvariablen:@@address,@@class_var

Globale Variablen:$debug,$CUSTOMER

(18)

1 Einführung 1.17 Methodennamen [23]

Klassennamen:String,MyClass,DataBaseHandler

Konstanten:SPEED_LIMIT,DEBUG

DieNamenskonventionenfür die Benennung von Variablen unterscheiden sich von denen in Java.

Zusätzlich gibt es noch die Zeichen@und$, die zur Markierung von speziellen Variablentypen verwendet werden.

Lokale Variablenwerden grundsätzlich kleingeschrieben. Wenn sie aus mehreren Worten bestehen, werden diese durch einen Underscore (_) voneinander getrennt.

Methodennamenwerden analog zu den lokalen Variablen benannt.

Instanzvariablenwerden ebenfalls wie lokale Variablen benannt, allerdings muss ein@voran- gestellt werden. Details zu Instanzvariablen werden später noch erläutert.

Klassenvariablenwerden wie lokale Variablen benannt, allerdings muss ein@@vorangestellt werden. Auch dieses Thema wird noch vertieft.

Globale Variablenwerden wie lokale Variablen benannt, allerdings muss ein$vorangestellt werden. Globale Variablen sollten nur sehrbegrenztbenutzt werden.

Klassennamenwerden (wie in Java) in der sog. Camel-Notation angegeben. Die beginnen mit einem Großbuchstaben. Wenn sie aus mehreren Wörtern bestehen, werden diese durch einen weiteren Großbuchstaben markiert.

Konstantenwerden (wie in Java) nur in Großbuchstaben benannt. Wörter werden durch Underscore (_) getrennt.

1.17 Methodennamen [23]

Methoden können Sonderzeichen enthalten

Konvention für letztes Zeichen des Namens

!: Methode verändert das Objekt oder ist „gefährlich“

?: Methode liefert einen booleschen Wert

=: Methode steht links in einer Zuweisung

Eine weitere Besonderheit von Ruby ist, dass Sonderzeichen in Methodennamen erlaubt sind.

Hierbei gibt es einige Konventionen: Methoden, die mit!enden gelten als „gefährliche“ Methoden, d. h. es handelt sich um Methoden, die das Objekt, auf dem sie aufgerufen werden verändern. Häufig gibt es dann zwei Varianten einer Methode, eine mit!und eine ohne. Die Methode ohne!verändert das Objekt nicht und gibt ein neues Objekt zurück, die mit!verändert es.

s = "Hallo"

t = s.gsub(/l/, '') puts s #=> 'Hallo' puts t #=> 'Hao'

Ruby Grundlagen Seite 15

(19)

1 Einführung 1.18 Zeilenorientierung [24]

s.gsub!(/l/, '') puts s #=> 'Hao'

Methoden, die auf ein?enden, liefern immer einen booleschen Wert zurück und können daher direkt in einer Kontrollstruktur verwendet werden.

a = [ 'a', 'b', 'c' ] if a.include?('a')

puts "'a' gefunden"

end

Methoden mit einem= im Namen entsprechen den sog. „Settern“ in Java, d. h. es handelt sich um Methoden, die einem Attribut eines Objektes einen Wert zuweisen. Hier erlaubt es Ruby, das Gleichheitszeichen mit einem Leerzeichen abzutrennen, sodass der Methodenaufruf wie eine Zuweisung aussieht.

class A

# Setter

def name=(name)

@name = name end

# Getter def name

@name end end a = A.new

a.name = 'Thomas' puts a.name #=> Thomas

1.18 Zeilenorientierung [24]

Ruby istzeilenorientiert

Ausdrücke enden mit der Zeile

Zeilenende kann mit\escaped werden

Wenn dem Parser klar ist, dass das Statement nicht beendet ist, kann der\entfallen

Mehrere Statements in einer Zeile werden mit;getrennt

(20)

1 Einführung 1.19 BEGIN und END-Blöcke [25]

a = 1 b = 2; c = 3

d = 4 + 5 + # kein \ nötig 6 + 7

e = 8 + 9 \ + 10

Anders als in Java, muss man in Ruby ein Statement nicht explizit mit einem;beenden, sondern Ruby geht davon aus, dass jedes Statement am Ende der Zeile beendet ist.

Will man davon abweichend mehrere Statements in eine Zeile schreiben, muss man sie durch; trennen. Das ist aber in Ruby sehr unüblich, sodass die Regel gilt:ein Statement, eine Zeile. In dieser Vorlesung kommt diese Schreibweise manchmal vor aber nicht, um diesen Stil zu propagieren, sondern damit der Quelltext auf eine Folie passt.

Manchmal will man ein Statement aus optischen Gründen auf mehrere Zeilen verteilen. Generell kann man durch einen Backslash (\) am Ende der Zeile signalisieren, dass man das Statement fortsetzen will. Wenn Ruby aufgrund der Syntax erkennen kann, dass das Statement noch nicht beendet sein kann, z. B. weil noch eine Klammer zu schließen ist oder ein Operand fehlt, kann man auf den Backslash verzichten. In den meisten Fällen von mehrzeiligen Statements kann er daher entfallen.

def viele_parameter(*a) end

viele_parameter(1, 2, 3, 4, 5, 6, 7, 8)

1.19 BEGIN und END-Blöcke [25]

Quelltext kannBEGIN- undEND-Blöcke enthalten

BEGIN-Blöcke werden ausgeführt, wenn die Datei geladen wird

END-Blöcke werden nach dem Ende des Programms ausgeführt BEGIN {

puts 'Anfang' }

puts 'In der Mitte' END {

Ruby Grundlagen Seite 17

(21)

1 Einführung 1.19 BEGIN und END-Blöcke [25]

puts 'Ende' }

(22)

Kapitel 2

Kontrollstrukturen

2.1 Überblick [27]

Ruby hat alsKontrollstrukturen

if,elsifundelse

unless,else

case

Die Kontrollstrukturen von Ruby sind ähnlich zu anderen Programmiersprachen. Man beachte, dass es in Rubyelsifheißt und nichtelse ifwie in Java oderelifwie in Python. Ein Spezialfall ist dasunless, das weiter unten noch genauer betrachtet wird.

2.2 if [28]

Bedingungen mitif,elsifundelse

ifwird mitendbeendet

thenwird nur benötigt, wennifund Code in einer Zeile stehen Syntax

if condition [ then ] code

elsif condition [ then ] code

elsecode end

Dieif-Bedingungin Ruby unterscheidet sich nicht von anderen Programmiersprachen. Die einzige Besonderheit ist, dass es für denelse-if-Zweig ein eigenes Schlüsselwortelsifgibt. In Java, C

Seite 19

(23)

2 Kontrollstrukturen 2.2 if [28]

und C++ existieren gar keine speziellen else-if_-Zweige, sondern diese werden einfach durch die Kombination eineselsemit einem darauffolgendenifsimuliert.

Dasthenimifist optional. Man kann es verwenden, wenn die Bedingung in einer Zeile geschrieben werden soll und man kein;verwenden möchte. Ansonsten wird es häufig einfach weggelassen.

a = 5

# Variante mit then

if a < 4 then puts 'kleiner 4' end

# Variante ohne then

if a < 4; puts 'kleiner 4' end

# Mehrzeilig, normalerweise ohne then if a < 4

puts 'kleiner 4' end

Beispiel: if

today = Time.now if today.saturday?

puts "Gartenarbeit"

elsif today.sunday?

puts "Entspannen"

else

puts "Hochschule"

end

if als modifier

puts "Arbeiten" if !Time.now.sunday?

Ruby erlaubt es, die meisten Kontrollstrukturen als sogenannteModifierzu verwenden, d. h. die Kontrollstruktur wird hinter das Statement geschrieben und „modifiziert“ es insofern als dass es nur ausgeführt wird, wenn die Bedingung erfüllt ist. Das wirkt auf den ersten Blick ungewohnt, hat aber den großen Vorteil, dass die Kontrollstrukturen den Blick auf die eigentliche Programmlogik nicht verstellen, insbesondere, wenn das Statement im Normalfall ausgeführt wird.

Beispiel: if

today = Time.now su = today.sunday?

sa = today.saturday?

(24)

2 Kontrollstrukturen 2.3 unless [31]

if sa then puts "Gartenarbeit" elsif su then puts "Entspannen" end

2.3 unless [31]

Negative Bedingungen können mitunlessanstattifausgedrückt werden

Keinelsif unless a.nil?

puts "a ist nicht nil"

else

puts "a ist nil"

end

unless als modifier

puts "Arbeiten" unless Time.now.sunday?

Eine absolute Besonderheit von Ruby sind spezielle Kontrollstrukturen für die Negation der Be- dingung, hier dieunless-Bedingung. Diese Kontrollstrukturen führen den dazugehörigen Code aus, wenn die Bedingungnichtwahr ist. D. h. in Ruby schreibt man üblicherweise nichtif !a, sondern unless a. Es gibt allerdingskeineelse-unless-Konstruktion.

Somit entspricht der Code im Beispiel oben:

if !a.nil?

puts "a ist nicht nil"

else

puts "a ist nil"

end

2.4 Was ist true? [32]

truthiness

Nurnilundfalsefalse

Alle anderen Werte sindtrue

Ruby Grundlagen Seite 21

(25)

2 Kontrollstrukturen 2.5 Double Bang Operator [33]

a = nil

if a then puts 'Wert ist true' else puts 'Wert ist false' end b = ""

if b then puts 'Wert ist true' else puts 'Wert ist false' end

Ausgabe

Wert ist false Wert ist true

Die Frage, wann ein Ausdruck zutrueevaluiert wird, ist in einigen Skriptsprachen sehr unglücklich geregelt. So gibt es zu der Frage, was in JavaScript alles wahr ist lange Abhandlungen.

So sind inJavaScriptz. B.false

false

0

=0

NaN

null

undefined

''(der leere String)

Ruby hat sich für eine sehr einfache Lösung entscheiden: Es ist allestrueaußer genau zwei Werten, nämlichfalseselbst undnil. Man spricht hier auch von dertruthiness.

Hierbei muss man darauf achten, dass sich Ruby-Programmierer oft nicht die Mühe machen, zwischennilundfalsezu unterscheiden, da beises sowieso falsch ist. D. h. Funktionen, aus denen man vielleicht einen Wahrheitswert erwartet, kommt einnil. Dies ist so lange kein Problem, wie man den Rückgabewert nur mitifetc. verarbeitet. Braucht man auf jeden Fall einen booleschen Wert, bietet sich die Verwendung des Double Bang Operators !! an, der einen Wert in einen booleschen umwandelt.

2.5 Double Bang Operator [33]

double bang operator!!

wandelt jeden Wert in einen booleschen Wert um

technisch eine doppelte Negation (!)

a = [ !!0, !!"", !!"h", !!nil, !!true, !!false, !![], !!{}, !! lambda {} ] puts a.inspect

(26)

2 Kontrollstrukturen 2.6 Case [34]

Ausgabe

[true , true , true , false , true , false , true , true , true ]

Da man in Ruby sowieso alle Ausdrücke in Kontrollstrukturen verwenden kann, muss man viel seltener als in Java eine Konvertierung in einen booleschen Wert vornehmen. Will man dies aber trotzdem machen, verwendet man dendouble bang operator!!, der genaugenommen einfach nur eine doppelte Negation ist. Auf jeden beliebigen Ruby-Ausdruck angewandt, bekommt man dann dessen Wahrheitswert, gemäß den erläuterten Regeln zur Truthiness, zurück.

2.6 Case [34]

Case-Statement ähnlich zu anderen Sprachen

keinbreaknötig

Syntax

case VARIABLE when WERT [then ] when WERT [ then ]...

...

else...

end

Dascase-Statement von Ruby ist sehr ähnlich zu dem von Java, mit dem Unterschied, dass 1. dasswitchaus Java in Rubycaseheißt,

2. dascaseaus Java in Rubywhenheißt, 3. dasdefaultaus Java in Rubyelseheißt,

4. man in Ruby keinbreakbenötigt, weil es keine sog.fall-through logicgibt, d. h. es werde nichtdie folgendenwhenStatements ebenfalls ausgeführt und

5. man nicht nur Ganzzahlen und Strings imwhenverwenden kann, sondern jeden Datentyp, der die===-Methode überschreibt.

Die Verwendung desthenist analog zuif: Es ist nur nötig, wenn Bedingung und Aktion in einer Zeile stehen und man das Semikolon (;) sparen möchte.

Beispiel: case

age = 3 case age

Ruby Grundlagen Seite 23

(27)

2 Kontrollstrukturen 2.6 Case [34]

when 1, 2 puts 'Baby' when 3, 4

puts 'Kleinkind' elseputs 'Kind' end

Ausgabe

Kleinkind

In Java sähe dieselbe Auswahl wie folgt aus.

int age = 3;

switch (age) { case 1:

case 2:

System.out.println("Baby");

break;

case 3:

case 4:

System.out.println("Kleinkind");

break;

default:

System.out.println("Kind");

}

Statement gibt selbst wieder einen Wert zurück, d. h. es wird evaluiert

age = 3

bezeichnung = case age when 1, 2 then 'Baby' when 3, 4 then 'Kleinkind' else 'Kind'

end

puts bezeichnung

(28)

2 Kontrollstrukturen 2.7 Case und Ranges [37]

Ausgabe

Kleinkind

Eine sehr praktische Eigenschaft von allen Kontrollstrukturen in Ruby ist, dass sie selbst ein Aus- druck sind, der einen Wert liefert. Man muss also in den Ästen gar keine Zuweisung an eine Variable machen, sondern kann direkt den Wert angeben, der von der Kontrollstruktur zurückgege- ben werden soll, wenn der jeweilige Ast genommen wird. Dadurch vereinfachen sich viele typische Aufgaben erheblich.

Ohne dieses Feature müsste man das obige Programm schreiben als:

age = 3 case age

when 1,3

bezeichnung = 'Baby' when 3,4

bezeichnung = 'Kleinkind' elsebezeichnung = 'Kind' end

2.7 Case und Ranges [37]

Dascase-Statement kann gut zusammen mit Ranges verwendet werden

value = 79

as_string = case value when 1..50 then "low"

when 51..75 then "medium"

when 76..100 then "high"

else "unknown"

end

puts as_string # -> high

In Java müsste man an dieser Stelle auf einswitchverzichten und das Ganze als (fehleranfällige) if-Anweisung schreiben:

Ruby Grundlagen Seite 25

(29)

2 Kontrollstrukturen 2.7 Case und Ranges [37]

int value = 79;

String asString;

if (1 <= value && value <= 50) { asString = "low";

}else if (51 <= value && value <= 75) { asString = "medium";

}

else if (76 <= value && value <= 100) { asString = "high";

} else {

asString = "unknown";

}

System.out.println(asString); // -> high

Wehe man die Werte passen nicht aneinander oder man verwechselt ein<=mit einem<; schon gibt es schwer zu findende Fehler, die nur unter seltenen Bedingungen auftreten.

(30)

Kapitel 3

Schleifen

3.1 Kopfgesteuerten while-Schleife [39]

Syntax der kopfgesteuertenwhile-Schleife(while-loop)

while conditional [do]

endcode

doist optional, außer die Schleife wird in einer Zeile formuliert

i = 0 while i < 5

puts "i ist #{i}"

i +=1 end

Obwohl es in Ruby die in der Programmierung üblichen Schleifen gibt, wird man in echten Ruby- Programmen nur selten Schleifen antreffen. In Ruby ist es üblich, die allermeisten Probleme über Iteratoren zu lösen, d. h. über das Anwenden einer Funktion auf alle Elemente einer Collection (Array oder Hash). Dieses Vorgehen wird im Kapitel zu Containern näher erläutert.

Das obige Beispiel würde man in Ruby normalerweise wie folgt ohne Schleife über einen Iterator schreiben:

5.times { |i| puts "i ist #{i}" }

Dieser Ansatz stammt aus der Funktionalen Programmierung. Man spricht auch von einerinternen Iterationwohingegen die normalen Schleifen mitwhileetc. eineexterne Iterationdarstellen. Der große Vorteil einer internen Iteration besteht darin, dass sie

Seite 27

(31)

3 Schleifen 3.2 while als Modifier [40]

weniger fehleranfällig ist, weil keine Indices von Hand geführt werden müssen und

parallelisierbar ist, d. h. im Zusammenhang mit der parallelen Programmierung besser ver- wendet werden kann.

Die Parallelisierbarkeit ergibt sich daraus, dass ohne eine äußere Iteration die übergebene Funktion keine Änderungen machen kann, die mehr als ein Element betreffen. Außerdem kann – wenn die Funktion zusätzlich noch seiteneffektfrei ist – sicher davon ausgehen, dass eine Aufspaltung der Operation auf mehrere parallele Threads dasselbe Ergebnis bietet, wie eine lineare Ausführung. Das Beispiel oben ist übrigens nicht frei von Seiteneffekten, weil die Ausgabe perputsein Seiteneffekt ist. Bei einer Parallelisierung würden die Werte nicht mehr in der korrekten Reihenfolge 1, 2, 3 . . . ausgegeben.

Frei von Seiteneffekten wäre aber z. B. folgende interne Iteration, die ausschließlich die Elemente des Arraysaverändert:

a = [1, 2, 3]

b = a.map { |e| e**2 }

3.2 while als Modifier [40]

whilekann wieforoderuntilauch als Modifier benutzt werden

while in normalen Form

while i < 5 do i += 1 end

while als modifier

i += 1 while i < 5

Wie auch die Kontrollstrukturen kann man die Schleifen hinter das Statement schreiben, auf das sie sich beziehen sollen. Auch diese Konstruktion ist aber wegen der Dominanz der internen Iteration in Ruby eher selten.

3.3 Fußgesteuerte while-Schleife [41]

Syntax der fußgesteuertenwhile-Schleife(while-loop)

begin

endcodewhile conditional

(32)

3 Schleifen 3.4 until-Schleife [42]

i = 9 begin

puts "i ist #{i}"

i += 1 end while i < 5

Ausgabe

i ist 9

3.4 until-Schleife [42]

until-Schleife

läuft, solange die Bedingung falsch ist

until Aentsprichtwhile !A

i = 0 until i == 5

puts "i ist #{i}"

i +=1 end

i = 0 begin

puts "i ist #{i}"

i += 1

end until i == 5

Dieuntil-Schleife entspricht derwhile-Schleife mit dem Unterschied, dass die Bedingung negiert wird, d. h. die Schleife läuft so lange die Bedingungnichtzutrifft. Damit istuntil Aäquivalent mit while !A, wobeiAein Ausdruck ist, der sich als Wahrheitswert interpretieren lässt.

Ruby Grundlagen Seite 29

(33)

3 Schleifen 3.5 for-Schleife [43]

3.5 for-Schleife [43]

Syntax derfor-Schleife

for variable [, variable ...] in expression [do]

endcode

Beispiel

for i in 0..5 puts "i ist #{i}"

end

Ausgabe

i ist 0 i ist 1 i ist 2 ...

words = [ 'Hallo', 'World', '!' ] for w in words

puts "w: #{w}"

end

Ausgabe

w: Hallo w: World w: !

Diefor-Schleife in Ruby ist deutlich weniger leistungsfähig als das entsprechende Konstrukt in Java oder C. In Ruby kann sie ausschließlich über die Elemente einer Collection oder einen Range laufen. Die Angabe einer Laufvariable oder das eigenständige Modifizieren dieser Variable ist nicht möglich. Sie entspricht damit eher der sog. for-each-Schleife in Java als der for-Schleife.

Das obige Beispiel würde in Java wie folgt aussehen:

String[] words = { "Hallo", "World", "!" };

for (String word : words) {

System.out.println("w: " + word);

}

(34)

3 Schleifen 3.6 Kontrolle der Schleife [45]

3.6 Kontrolle der Schleife [45]

Schlüsselworte für dieKontrolle der Schleife

break: verlässt die innerste Schleife

next: startet die nächste Iteration der innersten Schleife

redostartet die nächste Iteration der innersten Schleifeohne die Bedingung zu überprüfen oder den Zähler zu erhöhen

Dasbreak-Statement in Ruby entspricht dem von Java und hat auch dasselbe Verhalten. Allerdings besitzt Ruby keinelabeled break, d. h. man kann immer nur die aktuelle Schleife verlassen, nicht aber eine weiter außen liegende. Will man aus mehreren Schleifen heraus-„breaken“, muss man eine zusätzliche Variable verwenden und diese in jeder umgebenden Schleife abprüfen.

Beispiel: break

for i in 0..5 if i > 2

break end

puts "Wert von i ist #{i}"

end

Ausgabe

Wert von i ist 0 Wert von i ist 1 Wert von i ist 2

Dasnext-Statement in Ruby entspricht demcontinue-Statement in Java.

continue in Java

for (int i = 0; i <= 5; i++) { if (i < 2) {

continue;

}System.out.println("Wert von i ist " + i);

}

Beispiel: next

for i in 0..5 if i < 2 endnext

puts "Wert von i ist #{i}"

end

Ruby Grundlagen Seite 31

(35)

3 Schleifen 3.7 Scope der Variablen [49]

Ausgabe

Wert von i ist 2 Wert von i ist 3 Wert von i ist 4 Wert von i ist 5

Dasredo-Statement in Ruby springt nicht zum Schleifenkopf, sondern zum ersten Statementdanach.

Insofern wird weder die Schleifenbedingung überprüft, noch bei derfor-Schleife die Laufvariable weiterbewegt. Die Anwendungsbereiche fürredo sind beschränkt und bei der Nutzung lauert immer die Gefahr einer Endlosschleife.

Beispiel: redo

for i in 0..5 if i < 2

puts "Wert von i ist #{i}"

redo endend

Ausgabe

Wert von i ist 0 Wert von i ist 0 Wert von i ist 0 ...

3.7 Scope der Variablen [49]

Schleifen schaffen keinen neuen Scope, d. h. Variablen sind auch nach der Schleife noch sichtbar

Variablen in Blöcken sind lokal zum Block (es sei denn, sie waren schon vorher definiert)

i = 0 while i < 5

my_i = i i += 1 end

puts my_i # 4

(36)

3 Schleifen 3.7 Scope der Variablen [49]

5.downto(1) { |v| my_v = v } puts my_v

# => NameError: undefined local variable or method `my_v' for main:Object

DerScope von Variablenin Ruby unterscheidet sich von Java. In Java sind Variablen immer lokal zu dem Java-Block, in dem sie definiert wurden. In Ruby hingegen schaffen Schleifen und Kon- trollstrukturen keinen neuen Scope (Sichtbarkeitsbereich). Damit sind die Variablen auch nach der Schleife bzw. Kontrollstruktur noch sichtbar.

Scope in Java

for (int i = 0; i < 10; i++) { // tu was

int k = i * 2;

}// weder k noch i sind hier noch sichtbar

Ein Ruby-Block ist ein eigener Sichtbarkeitsbereich. Allerdings hat er technisch nichts mit dem Java-Block zu tun:

EinJava-Blockgruppiert eine beliebige Anzahl von Statements so, dass sie wie ein einzelnes Statement verwendet werden können.

EinRuby-Blockdefiniert ein Stück ausführbaren Code, der an eine Methode übergeben werden kann. Er entspricht daher viel eher demJava-Lambdaals einem Block in Java.

Ruby-Blöcke werden noch ausführlich im Kapitel zu Containern behandelt.

Ruby Grundlagen Seite 33

(37)

Kapitel 4

Ausdrücke

4.1 Alles hat einen Rückgabewert [51]

Wo immer möglich geben Ruby-Statements einen Wert zurück

a = b = c = 0 # => 0

[ 3, 1, 7, 0 ].sort.reverse # => [7, 3, 1, 0]

x = if a == 0 'Null' else

'Nicht Null' end

puts x # => "Null"

Wie bereits erläutert, sind in Ruby alle Daten ein Objekt. Genauso konsequent hat alles in Ruby einen Rückgabewert, z. B. Kontrollstrukturen, Klassendefinitionen, Methodendefinition, Zuweisungen etc.

Klassendefinition→nil

Methodendefinition→Name der Methode als Symbol

Zuweisung→Wert rechts vom Gleichheitszeichen

Durch diese Eingenschaft kann man an vielen Stellen die Programmierung vereinfachen.

4.2 Operatoren als Methoden [52]

Die meisten Operatoren (z. B.+,*,=) in Ruby sind als Methoden realisiert

Operatoren der Standardklassen können überschrieben werden (Vorsicht!)

Klassen können Operatoren nach den eigenen Bedürfnissen implementieren

(38)

4 Ausdrücke 4.3 Beispiel: Operatoren implementieren [53]

a, b, c = 1, 2, 3 a * b + c # => 5 (a.*(b)).+(c) # => 5

Ruby implementiertOperatorenselbst wieder als Methoden. Durch die syntaktische Besonderheit, dass man zusätzlich den Punkt bei der Benutzung einer solchen Methode weglassen kann, sehen diese Methoden aus, wie die Operatoren anderer Programmiersprachen. Anstatta + bkann man daher auch schreibena.+(b), was zeigt, dass+eine ganz normale Methode, mit einem sehr kurzem und speziellem Namen, ist.

Da die Operatoren in den Standardklassen (z. B. Integer oder Float) als Methoden implementiert sind, kann man diese überschreiben. Allerdings sollte man dies nur mit größter Vorsicht tun und vor allem darauf achten, dass man das normale Verhalten nicht verändert.

# Schlechtes Beispiel class Integer

# Plus Operator ersetzen def +(other)

self * other endend

puts 5 + 5 #=> 25

4.3 Beispiel: Operatoren implementieren [53]

class Cout def <<(str)

print str self endend

cout = Cout.new

cout << "Hallo" << " Welt" << "!" << "\n"

Ausgabe

Hallo Welt!

Ruby Grundlagen Seite 35

(39)

4 Ausdrücke 4.4 Betriebssystem-Kommandos [55]

Dieses Beispiel zeigt, wie man in einer eigenen Klasse einen Operator implementiert. Es simuliert das Verhalten vonstd::coutaus<iostream>in C++. Hier können beliebige Strings über den

<<-Operator auf der Konsole ausgegeben werden.

Der Operator wird einfach durch die Methode mit dem entsprechenden Namen<<implementiert.

Damit die Aufrufe aneinander gereiht werden können, gibt die Methode als letztes eine Referenz auf das eigene Objekt zurück. Hierdurch kann man beliebig viele Aufrufe von<<hintereinander- schreiben – eine Technik, die manmethod chainingnennt.

Das nächste Beispiel zeigt, wie man den[]-Operator implementieren kann.

class Sum def [](index)

sum = 0

1.upto(index).each { |e| sum += e } endsum

end

sum = Sum.new

puts sum[100] # => 5050

Hier ist zu beachten, dass die Methode[]heißt, obwohl der Parameter dann später zwischen den eckigen Klammern steht. Dieses Beispiel gibt die Summe der Zahlen bis zum angegebenen Wert zurück. Man würde im Beispiel eigentlich diereduce-Methode benutzen, die aber erst im Kapitel zu Containern eingeführt wird. In diesem Fall sähe das Beispiel wie folgt aus, greift aber sehr weit vor:

class Sum def [](index)

(1..index).reduce(:+) endend

4.4 Betriebssystem-Kommandos [55]

Mit dem Backtick “‘ oder%x{ }eingeschlossene Strings werden vom Betriebssystem ausge- führt

Die Ausgabe des Kommandos ist der Rückgabewert

Der Exit-Code ist in der Variable$?(TypProcess::Status) zu finden d = `date`

puts d # => "Do 15 Jun 2017 23:11:53 CEST\n"

Referenzen

ÄHNLICHE DOKUMENTE

Listing available classes and modules Arguments and locks in call signatures Class method documentation. Where Ruby docs come from: rdoc Exceptions: Handling

Listing available classes and modules Arguments and locks in call signatures Class method documentation. Where Ruby docs come from: rdoc Exceptions: Handling

Listing available classes and modules Arguments and locks in call signatures Class method documentation. Where Ruby docs come from: rdoc Exceptions: Handling

Listing available classes and modules Arguments and locks in call signatures Class method documentation. Where Ruby docs come from: rdoc Exceptions: Handling

Listing available classes and modules Arguments and locks in call signatures Class method documentation. Where Ruby docs come from: rdoc Exceptions: Handling

Listing available classes and modules Arguments and locks in call signatures Class method documentation. Where Ruby docs come from: rdoc Exceptions: Handling

Listing available classes and modules Arguments and locks in call signatures Class method documentation. Where Ruby docs come from: rdoc Exceptions: Handling

Roland Schneider, Bundesministerium für Kunst, Kultur, öffentlichen Dienst und Sport. Zeit