5.5 Funkcje

# Funkcje

Gdy już zaczniesz pisać znaczne ilości kodu, to w pewnym momencie 
będziesz chciał znaleźć sposób aby uporządkować i poukładać go tak 
aby był one elegancki i łatwiejszy do zrozumienia. Funkcje są bardzo 
skutecznym sposobem, który to umożliwia. Pozwalają nam nadać kawałkowi 
kodu nazwę. Rzućmy na to okiem.

## Definiowane Funkcji

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

W powyższym przykładzie zdefiniowaliśmy nową funkcję zwaną `foo`. Zrobiliśmy 
to używając naszego dobrego znajomego bloku do/end oraz magicznego słowa 
`define`, po którym podaliśmy nazwę, którą chcemy nadać naszej funkcji. 
Nie musieliśmy nazywać jej `foo`, mogliśmy nazwać ją jak tylko mamy ochotę, 
np. `bar`, `baz` albo idealnie pasowałoby, gdybyśmy nadali jakąs nazwę, 
która coś znaczy, np. `czesc_glowna` albo `glowna_gitara`.

Pamiętaj tylko aby poprzedzić nazwę dwukropkiem `:` gdy definiujesz nową 
funkcję.

## Uruchamianie Funkcji

Skoro już udało się nam zdefiniować naszą funkcję, możemy uruchomić ją 
wpisując jej nazwę: 

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

foo

sleep 1

2.times do
  foo
end
```

Możemy użyć naszej funkcji `foo` wewnątrz bloku iteracji albo gdziekolwiek 
indziej, gdzie mogliśmy do tej pory używać polecenia `play` i `sample`. 
Pozwala nam to w bardzo fajny sposób na wyrażanie siebie i na tworzenie 
nowych słów, które znaczą coś nowego i używanie ich w naszych kompozycjach.

## Funkcje są zapamiętywane pomiędzy poszczególnymi uruchomieniami

Jak do tej pory, za każdym razem gdy nacisnąłeś przycisk Run, Sonic Pi 
zaczynał od czystej tablicy. Nie wiedział nic poza tym co znajduje się 
aktualnej przestrzeni roboczej. Nie możesz odnosić się do kodu w innych 
przestrzeniach roboczych lub innym wątku. Możliwość korzystania z funkcji 
zmienia to. Kiedy zdefiniujesz funkcję, Sonic Pi *zapamiętuje* ją. Spróbujmy 
to wykorzystać. Usuń cały kod znajdujący się w twojej aktualnej przestrzeni 
roboczej i zastąp go następującą linią: 

```
foo
```

Naciśnij przycisk Run - usłyszysz jak gra twoja funkcja. Skąd się wziął 
ten kod? Skąd Sonic Pi wiedział co powinien zagrać? Sonic Pi po prostu 
zapamiętał wcześniej twoją funkcję - więc nawet po tym jak już ją skasujesz 
z twojej przestrzeni roboczej, to będzie on pamiętał to co wcześniej 
napisałeś. Ten mechanizm działa tylko z funkcjami stworzonymi z wykorzystaniem 
polecenia `define` (oraz `defonce`).

## Funkcje z Parametrami

Być może zainteresuje Cie fakt, że tak samo jak możesz przekazywać wartości 
minimalną (min) i maksymalną (max) do funkcji `rrand`, tak samo możesz nauczyć 
twoją funkcję aby potrafiła przyjmować różne argumenty. Spójrzmy na następujący 
kod: 

```
define :my_player do |n|
  play n
end

my_player 80
sleep 0.5
my_player 90
```

Nie jest on za bardzo ekscytujący, ale świetnie przedstawia o co chodzi. 
Stworzyliśmy naszą własną wersję polecenia `play`, która przyjmuje 
parametr i nazwaliśmy ją `my_player`.

Parametry należy umieścić tuż za poleceniem `do` bloku kodu `define`, 
otoczyć pionowymi kreskami `|` oraz oddzielić przecinkami `,`. Możesz 
użyć dowolnego słowa jakiego chcesz dla nazw parametrów. 

Magia dzieje się w środku bloku do/end `define`. Możesz używać nazw 
parametrów tak jak byłyby prawdziwymi wartościami. W powyższym przykładzie 
gram nutę `n`. Możesz patrzeć na parametry jak na swego rodzaju obietnice, 
która mówi o tym, że gdy kod zostanie uruchomiony, to zostaną one zastąpione 
aktualnymi wartościami. Możesz to zrobić poprzez przekazanie parametru 
do funkcji gdy ją uruchamiasz. Uruchamiam polecenie `my_player 80` aby 
zagrać nutę 80. Wewnątrz definicji funkcji, `n` zostanie zamienione na 80, 
więc polecenie `play n` przemieni się w `play 80`. Kiedy uruchomię ją 
ponownie w taki sposób `my_player 90`, to teraz `n` zostanie zastąpione 
przez 90, więc polecenie `play n` będzie zmieni się teraz w polecenie 
`play 90`.

Przyjrzyjmy się teraz bardziej interesującemu przykładowi:

``` 
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

```

Użyłem tutaj parametru `repeats` w linii `repeats.times do` tak jakby 
był liczbą. Użyłem również parametru `root` dla polecenia `play` tak, 
jakby był normalną nazwą nuty. 

Zauważ, że poprzez przeniesienia dużej ilości logiki do funkcji, 
jesteśmy teraz w stanie napisać coś bardzo ekspresyjnego a zarazem łatwego 
do przeczytania!
