# 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, by był on 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 nim podaliśmy nazwę, którą chcemy nadać naszej funkcji. Nie musieliśmy nazywać jej `foo`, gdyż mogliśmy to zrobić tak, jak tylko mamy na to ochotę, np. `bar`, `baz`. Dobrym pomysłem jest nadanie im znaczących coś nazw, 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 Uruchom, 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 Uruchom - 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 jednej przestrzeni roboczej, to i tak będzie on pamiętał, 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 Cię 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 `,`. Dla nazw parametrów możesz użyć dowolnego słowa, jakiego chcesz.

Magia dzieje się w środku bloku do/end `define`. Możesz używać nazw parametrów, tak jakby były prawdziwymi wartościami. W powyższym przykładzie gram nutę `n`. Możesz patrzeć na parametry jak na swego rodzaju obietnice, które 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ą. Zrobiłem to samo z `root` dla polecenia `play` - 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!