Wenn du einmal damit angefangen hast größere Mengen Programm-Code zu schreiben, dann wirst du nach Wegen suchen, wie du die Dinge organisieren und strukturieren kannst, um sie sauberer und verständlicher zu machen. Funktionen sind ein sehr wirkungsvoller Weg das zu tun. Sie geben uns die Möglichkeit, einem Haufen Programm-Code 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 haupt_sektion
oder hintergrund_akkorde
.
Denke daran, bei der Definition einer Funktion ihrem Namen einen Doppelpunkt :
voranzustellen.
Wenn wir unsere Funktion definiert haben, können wir sie über die Eingabe ihres Namens 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 Blocks mit Iterationen verwenden - oder überall da, wo wir sonst auch play
oder sample
schreiben würden. Das gibt uns eine sehr weit gehende Möglichkeit uns auszudrücken, und können sinnvolle Worte einsetzen, um sie in unseren Kompositionen zu verwenden.
Wenn du bisher auf Ausführen
geklickt hast, ist Sonic Pi jedes Mal aufs Neue ohne irgendwelche Vorgaben gestartet. Es berücksichtigt dabei nichts, außer dem, was im jeweiligen Puffer steht. Du kannst dich nicht auf irgendwelchen Programm-Code beziehen, der in einem anderen Puffer oder in einem anderen Thread steht. Funktionen ändern das jedoch. Wenn du eine Funktion definierst, dann erinnert sich Sonic Pi daran. Probieren wir das aus. Lösche den gesamten Code in deinem Puffer und ersetze ihn durch:
foo
Klicke auf Ausführen
- und höre deine Funktion spielen. Wo wurde dieser Code gespeichert? Woher wusste Sonic Pi, was es zu spielen hat? Sonic Pi hat sich deine Funktion einfach gemerkt - sogar, nachdem du den Programm-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
) erzeugt hast.
Es wird dich vielleicht interessieren, dass so wie du rrand
einen Minimal- und Maximalwert übergeben kannst, du auch deinen Funktionen beibringen kannst Argumente zu akzeptieren. Sehen wir uns das an:
define :my_player do |n|
play n
end
my_player 80
sleep 0.5
my_player 90
Das ist 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 hinter dem do
des define
-do/end-Blocks stehen, umgeben von senkrechten Strichen (pipes) |
und durch Kommas ,
getrennt. Du kannst beliebige Wörter als Parameternamen verwenden.
Die Magie passiert innerhalb des define
-do/end-Blocks. 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 wenn der Programm-Code läuft, sie durch wirkliche Werte ersetzt werden. Du machst das, indem du der Funktion beim Aufruf einen Parameter mitgibst. Ich tue das hier mit my_player 80
, um die Note 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 jetzt play n
in play 90
verwandelt.
Sehen wir uns ein interessanteres Beispiel 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 in meinem Aufruf von play
wäre.
Sieh dir an, wie wir hier etwas sehr Ausdrucksstarkes und leicht zu Lesendes schreiben konnten, indem wir vieles von unserer Programmlogik in Funktionen verschieben!