# Funktionen

Wenn Du einmal damit angefangen hast, mehr Code zu schreiben, dann wirst Du nach Wegen suchen, wie Du die Dinge organisieren und strukturieren kannst. Damit wird alles ordentlicher und einfacher zu verstehen. Funktionen sind sehr praktisch, um das zu erreichen. Sie geben uns die Möglichkeit, einem Bündel von Codezeilen einen Namen zu geben. Sehen wir uns das an.

## Funktionen definieren

    define :foo do
      play 50
      sleep 1
      play 55
      sleep 2
    end

Hier haben wir eine neue Funktion mit dem Namen `foo` definiert. Wir machen das mit unserem alten Freund, dem do/end-Block und dem Zauberwort `define` gefolgt von dem Namen, den wir unserer Funktion geben möchten. Wir müssen die Funktion nicht unbedingt `foo` nennen, wir können sie auch irgendwie anders nennen; zum Beispiel `bar`, `baz` oder idealerweise einen für Dich bedeutsamen Namen wie `main_section` (etwa: Hauptteil) oder `lead_riff` (etwa: Leitfigur).

Denk daran, bei der Definition einer Funktion einen Doppelpunkt `:` vor ihren Namen zu stellen.

## Funktionen aufrufen

Wenn wir unsere Funktion definiert haben, können wir sie einfach über ihren Namen aufrufen:

    define :foo do
      play 50
      sleep 1
      play 55
      sleep 0.5
    end

    foo

    sleep 1

    2.times do
      foo
    end

Wir können `foo` sogar in Iterations-Blocks aufrufen und überall da, wo wir auch `play` oder `sample` hätten benutzen können. Das gibt uns eine große Ausdrucksfreiheit und wir können sinnvolle Worte bilden, die wir in unseren Kompositionen verwenden.

## Funktionen bleiben in Erinnerung

Wenn Du bislang den `Ausführen`-Button geklickt hast, startete Sonic Pi jedesmal aufs Neue ohne irgendwelche Vorgaben. Es berücksichtigt nichts, außer dem, was im jeweiligen Buffer steht. Du kannst Dich nicht auf irgendwelchen Code beziehen, der in einem anderen Buffer oder einem anderen Thread steht. Funktionen ändern das jedoch. Wenn Du eine Funktion definierst, dann erinnert sich Sonic Pi daran. Probieren wir das aus. Ersetze den den gesamten Code in Deinem Buffer durch:

    foo

Klick den `Ausführen`-Button und höre, wie Deine Funktion spielt. Wo wurde dieser Code gespeichert? Woher weiss Sonic Pi, was es zu spielen hat? Sonic Pi hat sich Deine Funktion einfach gemerkt. Also sogar, nachdem Du den Code aus dem Buffer gelöscht hast, wusste Sonic Pi noch, was Du geschrieben hattest. Dies funktioniert nur mit Funktionen, die Du mit `define` (und `defonce`) definiert hast.

## Funktionen parametrisieren

Du kannst Deinen Funktionen beibringen, Argumente zu übernehmen, genau so, wie Du z.B. `rrand` einen Minimal- und einen Maximalwert übergeben kannst. Sehen wir uns das an:

    define :my_player do |n|
      play n
    end

    my_player 80
    sleep 0.5
    my_player 90

Das ist jetzt nicht besonders aufregend, zeigt aber, worum es hier geht. Wir haben unsere eigene Version von `play` mit dem Namen `my_player` erschaffen. Diese ist parametrisiert - sie akzeptiert also Argumente.

Die Parameter müssen nach dem `do` stehen, welches zum `define` des do/end-Blocks gehört; sie werden von senkrechten Strichen (auch: Verkettungszeichen) umgeben und durch Kommata getrennt. Du kannst beliebige Worte als Parameternamen verwenden.

Die Zauberei findet innerhalb des `define`-do/end-Blocks statt. Du kannst die Parameternamen so benutzen, als wären sie wirkliche Werte. In diesem Beispiel spiele ich den Ton `n`. Du kannst die Parameter als eine Art Versprechen ansehen, dass sie durch wirkliche Werte ersetzt werden, wenn der Code läuft. Das machst Du, indem Du der Funktion beim Aufruf einen Parameter mitgibst. Ich tue das hier mit `my_player 80`, um den Ton 80 zu spielen. Innerhalb der Funktionsdefinition wird `n` nun durch 80 ersetzt, sodass `play n` sich in `play 80` verwandelt. Wenn ich die Funktion erneut mit `my_player 90` aufrufe, wird `n` durch 90 ersetzt, sodass sich `play n` in `play 90` verwandelt.

Sehen wir uns interessantere Beispiele an:

    define :chord_player do |root, repeats|
      repeats.times do
        play chord(root, :minor), release: 0.3
        sleep 0.5
      end
    end

    chord_player :e3, 2
    sleep 0.5
    chord_player :a3, 3
    chord_player :g3, 4
    sleep 0.5
    chord_player :e3, 3

Hier habe ich `repeats` so benutzt, als ob es eine Zahl in der Zeile `repeats.times do` wäre. Zusätzlich habe ich `roots` so verwendet, als ob es ein Notenname im Aufruf `play` wäre.

Siehst Du? Unser Code wird sehr aussagekräftig und leichter lesbar, wenn wir eine Menge der Programmlogik in Funktionen verschieben.