Dupa ce vei incepe sa scrii serios la cod, vei dori sa gasesti o cale sa organizezi si sa structurezi lucrurile astfel incat sa arate mai curat si sa fie usor de inteles. Functiile ofera o unealta foarte utila pentru aceasta. Ele ne ofera posibilitatea sa dam un nume unei bucati de cod. Sa aruncam o privire.
define :foo do
play 50
sleep 1
play 55
sleep 2
end
Aici am definit o functie noua, numita ‘foo’. Pentru asta folosim blocul do/end si cuvantul magic ‘define’ (defineste) urmat de numele pe care vrem sa-l dam functiei. Nu suntem obligati sa o numim ‘foo’, puteam sa ii spunem si ‘bar, ‘baz’ sau, mai bine, un nume care chiar inseamna ceva, gen ‘sectiune_principala’ sau ‘lead_riff’.
Tine minte sa treci intotdeauna doua puncte ‘:’ inaintea numelui functiei cand o definesti.
Odata ce am definit o functie, putem sa o apelam doar scriindu-i numele:
define :foo do
play 50
sleep 1
play 55
sleep 0.5
end
foo
sleep 1
2.times do
foo
end
Putem folosi ‘foo’ in interiorul unor iteratii sau oriunde am fi folosit ‘play’ sau ‘sample’. Aceasta ne ofera un mod interesat de a ne exprima si de a crea cuvinte noi pe care sa le folosim in compozitii.
Pana acum, de fiecare data cand ai apasat butonul Run, Sonic Pi a pornit de la zero. Nu stie nimic in afara de ceea ce se afla in buffer. Nu poti sa faci referire la codul din alt buffer sau din alt thread. Functiile schimba acest lucru. Cand definesti o functie, Sonic Pi o tine minte. Sa incercam. Sterge tot codul din buffer si inlocuieste-l cu:
foo
Apasa butonul Run si vei auzi functia cantand. Unde s-a dus codul? Cum a stiut Sonic Pi ce sa cante? Sonic Pi a memorat functia ta - deci si dupa ce ai sters-o din buffer, tine minte ce ai scris acolo. Acest comportament este valabil doar pentru functiile create folosind ‘define’ (si ‘defonce’).
Asa cum poti transmite functiei ‘rrand’ valorile pentru min si max, poti invata functiile tale sa accepte argumente. Sa aruncam o privire:
define :my_player do |n|
play n
end
my_player 80
sleep 0.5
my_player 90
Nu e prea palpitant, dar ilustreaza ce am spus. Am creat propria versiune pentru ‘play’, numita ‘my_player’, care accepta un argument.
Argumentele (parametrii functiei) trebuie sa urmeze dupa ‘do’ din blocul do/end ‘define’, incadrati de bare verticale ‘ | ’ si separati de virgule ‘,’. Poti utiliza ce cuvinte vrei pentru numele parametrilor. |
Partea magica se afla in interiorul blocului do/end ‘define’. Poti folosi numele parametrilor ca si cum ar fi valori reale. In acest exemplu, redau nota ‘n’. Poti considera parametrii ca un fel de promisiune ca la momentul executiei codului ei vor fi inlocuiti cu valori reale. Acest lucru se face transmitand parametrii functiei cand o apelezi. Eu am facut asta in linia ‘my_player 80’ pentru a reda nota 80. In cadrul definitiei functiei, ‘n’ este acum inlocuit cu 80, deci ‘play n’ se transforma in ‘play 80’. Cand voi apela din nou functia cu ‘my_player 90’, ‘n’ va fi inlocuit cu 90, deci ‘play n’ va deveni ‘play 90’.
Sa vedem un exemplu mai interesant:
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
Aici am folosit ‘repeats’ ca si cum ar fi fost un numar in linia ‘repeats.times do’. De asemenea am folosit ‘root’ ca si cand ar fi fost numele unei note cand am apelat ‘play’.
Iata cum am reusit sa scriem ceva foarte expresiv si usor de citit mutand o parte din cod intr-o functie!