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 dafür sehr praktisch. Sie geben uns die Möglichkeit, einem Bündel von Codezeilen einen Namen zu geben. Sehen wir uns das an.
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 erste_strophe
oder hintergrund_akkorde
.
Denk daran, bei der Definition einer Funktion einen Doppelpunkt :
vor ihren Namen zu stellen.
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.
Wenn Du bislang den Ausführen
-Button geklickt hast, startete Sonic Pi jedes mal aufs Neue ohne irgendwelche Vorgaben. Es berücksichtigt nichts, außer dem, was im jeweiligen Puffer steht. Du kannst Dich nicht auf irgendwelchen Code beziehen, der in einem anderen Puffer 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 gesamten Code in Deinem Puffer durch:
foo
Klick den Ausführen
-Button und höre, wie Deine Funktion spielt. Wo wurde dieser Code gespeichert? Woher weiß Sonic Pi, was es zu spielen hat? Sonic Pi hat sich Deine Funktion einfach gemerkt. Also sogar, nachdem Du den Code aus dem Puffer gelöscht hast, wusste Sonic Pi noch, was Du geschrieben hattest. Dies funktioniert nur mit Funktionen, die Du mit define
(und defonce
) definiert hast.
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 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!