EMZETT.
Login

Kurz: Methoden liefern den Wert des letzten Ausdrucks (return ist selten nötig).

Teil des Kurses Ruby

Kapitel 5 von 12 im Kurs Ruby. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

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).arity

Ausgabe:

Hallo, Welt!
Hi, Mia!
10
11
x {:farbe=>"rot", :groesse=>3}
1 9
49
-1

Namenskonventionen: ? 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
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) }.inspect

Ausgabe:

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| ... } und yield; block_given?
  • ->(x) { } (Lambda) und Proc.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

Weiter im Kurs

Zurück: Arrays, Hashes und Bereiche

Weiter: Klassen und Objekte

Alle Kapitel: Ruby im Überblick