Variablen & Datentypen
Ruby's Typsystem: dynamisch, objektbasiert, mit Symbolen, Ranges, Hash und Nil. Plus: Konstanten und die globale/Instance/Class-Variablen-Familie.
Inhaltsverzeichnis
Variablen & Datentypen in Ruby
Ruby ist dynamisch typisiert - du musst nie einen Typ angeben. Aber die Sprache hat reiche Typen unter der Haube, und alles ist ein Objekt.
Variablen zuweisen
name = "Anna"
alter = 28
pi = 3.14
aktiv = true
Variablennamen:
snake_caseals Konvention (nutzer_name, nichtnutzerName)- Beginnen mit Kleinbuchstabe oder Unterstrich
- Keine Typ-Deklaration
Die Objekt-Philosophie
In Ruby ist alles ein Objekt - selbst Zahlen:
42.class # => Integer
42.even? # => true
42.to_s # => "42"
42.times { puts "hallo" }
Keine Sonderfaelle, keine Primitive-vs-Object-Verwirrung.
Skalare Typen
Integer
a = 42
b = 1_000_000 # Unterstriche als Lesehilfe
c = 0xff # 255 (hex)
d = 0b1010 # 10 (binary)
a.class # => Integer
Ruby-Integers sind beliebig gross - keine Ueberlauf-Probleme:
2 ** 100 # 1267650600228229401496703205376
Float
pi = 3.14159
e = 2.718
gross = 1.5e3 # 1500.0
String
name = "Anna"
literal = 'kein #{interpolation}'
mehrzeilig = <<~TEXT
Zeile 1
Zeile 2
Zeile 3
TEXT
Der <<~TEXT-Heredoc entfernt die fuehrende Einrueckung - sehr praktisch.
Methoden:
"Anna".length # 4
"anna".upcase # "ANNA"
"Anna".reverse # "annA"
"Anna"[0..1] # "An" (Range)
"Hallo".include?("al") # true
Symbol
Ein Symbol ist wie ein unveraenderlicher String - effizient, weil Ruby identische Symbole nur einmal speichert:
:name
:status
:rails
Symbole werden als Keys in Hashes und als Labels gerne genutzt:
person = { name: "Anna", alter: 28 }
person[:name] # "Anna"
Boolean
true
false
Zusaetzlich: nil (das Ruby-Null) ist ein eigener Typ, kein null wie in anderen Sprachen:
nil.class # NilClass
nil.nil? # true
"".nil? # false
Truthy / Falsy
In Ruby gibt es nur zwei falsy-Werte: false und nil. Alles andere ist truthy - auch 0, "", []:
if 0
puts "true in Ruby!" # wird ausgegeben
end
if "".nil?
puts "nil"
else
puts "nicht nil" # wird ausgegeben
end
Compound-Typen
Array
namen = ["Max", "Anna", "Leo"]
namen[0] # "Max"
namen.last # "Leo"
namen.length # 3
namen << "Tim" # anfuegen
namen.push("Lea")
namen.first(2) # ["Max", "Anna"]
namen.include?("Anna") # true
Arrays koennen gemischte Typen enthalten:
bunt = [1, "text", :symbol, true, nil, [1, 2, 3]]
Hash
alter = {
"Anna" => 28,
"Max" => 34
}
# Mit Symbolen als Keys (bevorzugt in modernem Ruby)
alter = { Anna: 28, Max: 34 }
# Zugriff:
alter[:Anna] # 28
# Erweitern
alter[:Leo] = 22
alter.merge(Tim: 41)
# Pruefen
alter.key?(:Anna) # true
alter.empty? # false
Range
(1..5) # 1, 2, 3, 4, 5 (inklusiv)
(1...5) # 1, 2, 3, 4 (exklusiv)
(1..5).to_a # [1, 2, 3, 4, 5]
(1..5).each { |n| puts n }
("a".."e").to_a # ["a", "b", "c", "d", "e"]
Ranges funktionieren sogar mit Strings.
Konstanten
Variablen, die mit einem Grossbuchstaben beginnen, sind Konstanten:
MAX_BENUTZER = 100
PI = 3.14159
Einstellungen = { port: 3000 } # Konstanten koennen auch Hashes sein
# Aenderung gibt Warnung, aber Ruby erlaubt es technisch
# MAX_BENUTZER = 200 # warning: already initialized constant
Konvention: SCREAMING_SNAKE_CASE oder CapitalizedCamelCase (fuer Klassen).
Variable-Typen nach Scope
Ruby hat verschiedene Variable-Arten, erkennbar am Praefix:
lokal # lokale Variable
@instanz # Instanzvariable (in Klassen)
@@klasse # Klassenvariable
$global # globale Variable (meiden!)
KONSTANTE # Konstante
Im Detail:
- lokale Variable: nur im aktuellen Scope (Methode, Block)
@instanz: an eine Objekt-Instanz gebunden@@klasse: an eine Klasse gebunden (shared ueber Instanzen)$global: ueberall - fast immer ein schlechter Stil
Typkonvertierung
Ruby hat praktische .to_*-Methoden:
"42".to_i # 42
"3.14".to_f # 3.14
42.to_s # "42"
[1, 2, 3].to_s # "[1, 2, 3]"
# Streng: konvertiert oder raises Exception
Integer("42") # 42
Integer("abc") # ArgumentError
# Locker: konvertiert oder gibt 0/leer zurueck
"abc".to_i # 0
Die Integer("...")-Variante ist oft besser - sie scheitert laut bei ungueltigem Input.
Parallele Zuweisung
a, b = 1, 2
a, b = b, a # Tausch ohne Hilfsvariable
name, alter = ["Anna", 28]
first, *rest = [1, 2, 3, 4, 5] # first=1, rest=[2, 3, 4, 5]
nil-Behandlung
Sicher dereferenzieren: &.
Wie in anderen modernen Sprachen:
name = nutzer&.adresse&.plz
# nil, wenn eines der Zwischen-Objekte nil ist
Default mit ||=
einstellungen ||= {} # weise zu, wenn nil/false
Default mit ||
name = eingabe || "Anonym"
Aber: || reagiert auf jeden falsy-Wert (nil/false). Wenn du wirklich nur nil willst, nimm:
name = eingabe.nil? ? "Anonym" : eingabe
Praktisches Beispiel
def nutzer_beschreiben(data)
name = data[:name] || "Anonym"
alter = data[:alter]
rollen = data[:rollen] || []
alter_text = alter ? "#{alter} Jahre" : "Alter unbekannt"
rollen_text = rollen.empty? ? "keine Rollen" : rollen.join(", ")
"#{name} (#{alter_text}) - #{rollen_text}"
end
puts nutzer_beschreiben(name: "Anna", alter: 28, rollen: [:admin, :editor])
# Anna (28 Jahre) - admin, editor
puts nutzer_beschreiben(alter: 22)
# Anonym (22 Jahre) - keine Rollen
Zusammenfassung
- Ruby ist dynamisch, aber alles ist ein Objekt
- Symbolen (
:name) statt Strings fuer Keys und Labels - Nur
falseundnilsind falsy - alles andere truthy (sogar0!) - Arrays und Hashes sind die wichtigsten Collections
- Ranges (
1..10) sind eigene Typen - ebenfalls iterierbar &.fuer sichere Dereferenzierung,||=fuer Defaults- Konstanten beginnen mit Grossbuchstabe -
CapitalizedCamelCasefuer Klassen
Im naechsten Kapitel: Kontrollstrukturen - und die Ruby-Spezialitaeten wie unless und until.