Kurz: Methoden liefern den Wert des letzten Ausdrucks (return ist selten nötig).
Teil des Kurses Ruby
Methoden definieren
Methoden liefern den Wert des letzten Ausdrucks (return ist selten nötig).
def gruss(name = "Welt", anrede: "Hallo")
"#{anrede}, #{name}!"
end
def summe(*zahlen) # beliebig viele Argumente
zahlen.sum
end
def optionen(name, **opts) # Schlüsselwort-Argumente
"#{name} #{opts}"
end
def minmax(liste)
[liste.min, liste.max] # mehrere Werte als Array
end
def quadrat(x) = x * x # Einzeiler (Endless Method, Ruby 3)
puts gruss
puts gruss("Mia", anrede: "Hi")
puts summe(1, 2, 3, 4)
puts summe(*[5, 6]) # Array entpacken
puts optionen("x", farbe: "rot", groesse: 3)
klein, gross = minmax([4, 9, 1])
puts "#{klein} #{gross}"
puts quadrat(7)
puts method(:gruss).arityAusgabe:
Hallo, Welt!
Hi, Mia!
10
11
x {:farbe=>"rot", :groesse=>3}
1 9
49
-1Namenskonventionen: ? am Ende liefert true/false (leer?), ! verändert das Objekt (sort!), = definiert einen Setter.
Blöcke und yield
Ein Block ist ein Stück Code, das du einer Methode übergibst. Die Methode ruft ihn mit yield auf:
def wiederhole(n)
return enum_for(:wiederhole, n) unless block_given?
n.times { |i| yield i + 1 }
end
wiederhole(3) { |i| print "Lauf #{i}; " }
puts
p wiederhole(3).map { |i| i * 10 }
def mit_log(name)
puts "-> #{name} startet"
ergebnis = yield
puts "<- #{name} fertig: #{ergebnis}"
ergebnis
end
mit_log("Rechnung") { 6 * 7 }
def transformiere(liste, &block) # Block als Objekt
liste.map(&block)
end
p transformiere([1, 2, 3]) { |x| x + 100 }Ausgabe:
Lauf 1; Lauf 2; Lauf 3;
[10, 20, 30]
-> Rechnung startet
<- Rechnung fertig: 42
[101, 102, 103]Procs und Lambdas
Blöcke lassen sich als Objekte speichern:
quadrat = ->(x) { x * x } # Lambda
plus = lambda { |a, b| a + b }
pr = Proc.new { |a, b| "#{a.inspect} #{b.inspect}" }
puts quadrat.call(5), quadrat.(6), quadrat[7]
puts plus.call(2, 3)
puts pr.call(1), pr.call(1, 2, 3) # Proc: lockere Argumentprüfung
begin
plus.call(1)
rescue ArgumentError => e
puts "Lambda: #{e.message}"
end
verdopple = ->(x) { x * 2 }
inkrement = ->(x) { x + 1 }
puts (verdopple >> inkrement).call(5) # erst verdoppeln, dann +1
puts (verdopple << inkrement).call(5) # erst +1, dann verdoppeln
puts [1, 2, 3].map(&verdopple).inspect
puts verdopple.curry.(4)
add3 = ->(a, b, c) { a + b + c }.curry
puts add3.(1).(2).(3)
def zaehler
n = 0
-> { n += 1 } # Closure merkt sich n
end
c = zaehler
c.call; c.call
puts c.call
puts method(:puts).to_proc.lambda?Ausgabe:
25
36
49
5
1 nil
1 2
Lambda: wrong number of arguments (given 1, expected 2)
11
12
[2, 4, 6]
8
6
3
trueUnterschiede: Ein return in einer Lambda beendet nur die Lambda, in einem Proc die umgebende Methode; Lambdas prüfen die Argumentanzahl streng.
Rekursion und Memoisierung
def fakultaet(n) = n <= 1 ? 1 : n * fakultaet(n - 1)
def fib(n, memo = {})
return n if n < 2
memo[n] ||= fib(n - 1, memo) + fib(n - 2, memo)
end
puts fakultaet(20)
puts fib(80)
puts (1..10).map { |i| fib(i) }.inspectAusgabe:
2432902008176640000
23416728348467685
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]Merke
- Letzter Ausdruck = Rückgabewert; Standardwerte,
*args,**opts, Schlüsselwort-Argumente - Blöcke
{ |x| ... }undyield;block_given? ->(x) { }(Lambda) undProc.new: Code als Objekt,call,curry,>>- Closures merken sich ihre Umgebung
Übungsaufgabe
Schreibe eine Methode wende_an(liste, &block), die den Block auf jedes Element anwendet, und eine Lambda zum Quadrieren.
Quiz zur Selbstkontrolle
Was liefert eine Ruby-Methode ohne return?
- Den Wert des letzten Ausdrucks (richtig)
- nil
- Das erste Argument
- Einen Fehler
Wofür steht yield?
- Ruft den übergebenen Block auf (richtig)
- Beendet die Methode
- Definiert eine Methode
- Wirft einen Fehler
Wodurch unterscheidet sich ein Lambda von einem Proc?
- Lambdas prüfen die Argumentanzahl streng (richtig)
- Lambdas sind langsamer
- Es gibt keinen Unterschied
- Procs gibt es nicht mehr
Weiter im Kurs
Zurück: Arrays, Hashes und Bereiche
Weiter: Klassen und Objekte
Alle Kapitel: Ruby im Überblick