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).arityHallo, 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 }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?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 true
Unterschiede: 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) }.inspect2432902008176640000 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
Aufgabe
Schreibe eine Methode wende_an(liste, &block), die den Block auf jedes Element anwendet, und eine Lambda zum Quadrieren.