5.5 Funktionen

# 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.
