Wstęp do programowania

Funkcja

Author

Krzysztof Dyba

Funkcja

Funkcja to samodzielny blok kodu, który wykonuje określone zadanie. Funkcje pomagają przede wszystkim w organizacji kodu, czyniąc go modułowym i wielokrotnego użytku, dzięki czemu jest czytelniejszy i łatwiejszy do zrozumienia.

Do stworzenia funkcji używa się słowa kluczowego function() i następujących elementów:

  • nazwa funkcji – nazwa, która spowoduje wywołanie funkcji. Powinna odzwierciedlać to, co funkcja robi.
  • argumenty – dane wejściowe funkcji określone w okrągłych nawiasach (). Funkcja może mieć zero, jeden lub wiele argumentów.
  • ciało funkcji – blok kodu w nawiasach klamrowych {}, który definiuje działanie funkcji. Może zawierać obliczenia, pętle, instrukcje warunkowe i wywołania innych funkcji.
  • wynik funkcji – instrukcja return() powoduje zakończenie działania funkcji i zwrócenie jej wyniku. Jeśli nie został określony, to funkcja zwróci wartość ostatniego wyrażenia obliczonego w ciele funkcji (tzw. niejawny return). Ponadto, aby zwrócić wiele wartości, zazwyczaj łączy się je w listę.

Składnia prezentuje się w następujący sposób:

nazwa_funkcji = function(argument1, argument2) {
  # kod do wykonania (ciało funkcji)
  return(wynik)
}

Napiszmy prostą funkcję, która obliczy pole prostokąta.

pole_prostokata = function(a, b) {
  P = a * b
  return(P)
}

Aby użyć funkcji, należy ją wywołać i zdefiniować wymagane argumenty. Przyjmijmy, że długość boku a wynosi 10 cm, a wysokość boku b 5 cm. Wtedy:

pole_prostokata(a = 10, b = 5)
[1] 50

Funkcja może zwracać wiele wartości. Zobaczmy to na przykładzie obliczania statystyk opisowych wektora numerycznego (tj. średniej, mediany, wartości minimalnej oraz maksymalnej). Poszczególne statystyki zostaną zwrócone jako lista.

statystyki = function(x) {
  return(list(
    min = min(x),
    srednia = mean(x),
    mediana = median(x),
    maks = max(x)
  ))
}
statystyki(1:10)
$min
[1] 1

$srednia
[1] 5.5

$mediana
[1] 5.5

$maks
[1] 10

Argumenty

Pozycyjne

Z założenia kolejność podawanych argumentów jest zgodna z kolejnością w definicji funkcji.

potega = function(x, y) {
  return(x^y)
}
potega(x = 2, y = 3)
[1] 8
potega(2, 3)
[1] 8

potega(3, 2) to zupełnie inne działanie niż potega(2, 3) wykorzystując argumenty pozycyjne!

Nazwane

Podczas wywoływania funkcji można jawnie określić nazwy argumentów, co pozwala na podanie ich w dowolnej kolejności.

potega(x = 2, y = 3)
[1] 8
potega(y = 3, x = 2)
[1] 8

Domyślne

W definicji funkcji można nadać argumentom wartości domyślne. Jeśli użytkownik nie poda wartości dla tego argumentu, to zostanie użyta wartość domyślna.

# argument y ma określoną domyślną wartość
potega = function(x, y = 2) {
  return(x^y)
}
potega(2)
[1] 4
potega(2, 3)
[1] 8

Wielokropek

Za pomocą wielokropka ... można zdefiniować funkcję, która przyjmuje zmienną liczbę argumentów, co jest to przydatne w sytuacji, gdy nie wiadomo, ile argumentów zostanie przekazanych.

suma = function(...) {
  args = list(...)
  s = sum(unlist(args))
  return(s)
}
suma(1, 2, 3, 4, 5)
[1] 15

Innym istotnym zastosowaniem wielokropka ... jest przekazywanie argumentów do innej (wewnętrznej) funkcji wywoływanej w ramach bieżącej funkcji. Jest to szczególnie przydatne do pisania funkcji rozszerzających funkcjonalności istniejących funkcji. Zobaczymy działanie na przykładzie prostej funkcji, która wyświetli tekst z wybranym separatorem między wyrazami.

wyswietl_tekst = function(...) {
  tekst = paste(...)
  return(tekst)
}
# domyślny separator
wyswietl_tekst("Hello", "World")
[1] "Hello World"
wyswietl_tekst("Hello", "World", sep = "-")
[1] "Hello-World"

Zauważ, że w definicji funkcji wyswietl_tekst() nie zdefiniowaliśmy argumentu sep. W rzeczywistości, pochodzi on z funkcji paste().

Obsługa wyjątków

Obsługa wyjątków pozwala zarządzać nieoczekiwanymi problemami podczas wykonywania kodu, zapewniając, że program może z powodzeniem obsługiwać błędy i ostrzeżenia. Pozwala to przewidywać potencjalne problemy i odpowiednio je rozwiązać bez przerywania działania programu. Przyczyny błędów mogą być zróżnicowane, np. nieprawidłowy format danych wejściowych, brak pliku w określonej lokalizacji, dzielenie przez zero, itd.

Funkcja może zwrócić trzy stany:

  • błąd (error) – sytuacja krytyczna, która zatrzymuje wykonywanie programu (np. próba uzyskania dostępu do nieistniejącego obiektu).
  • ostrzeżenie (warning) – potencjalny problem, ale nie zatrzymuje wykonywania programu (np. wykonywanie nieprawidłowych obliczeń, które zwracają NaN (Not a Number)).
  • komunikat (message) – informacja, która nie wpływa na wykonanie programu (np. aktualizacja postępu).

Funkcja try() jest najprostszym sposobem obsługi błędów w R. Próbuje wykonać wyrażenie i jeśli wystąpi błąd (error), to nie zatrzymuje całego skryptu, ale zwraca obiekt klasy try-error. Następnie, możemy sprawdzić czy wystąpił błąd i podjąć odpowiednie działania. Argument silent = TRUE wstrzymuje wyświetlanie komunikatu o błędzie na konsoli (błąd jest nadal przechwytywany w zwracanym obiekcie).

wynik = try(sqrt("tekst"), silent = TRUE)
class(wynik)
[1] "try-error"
if (inherits(wynik, "try-error")) {
  cat("Wystąpił błąd:", "\n", wynik)
} else {
  wynik
}
Wystąpił błąd: 
 Error in sqrt("tekst") : non-numeric argument to mathematical function

Do bardziej zaawansowanej kontroli służy funkcja tryCatch(), która obsługuje zarówno błędy i ostrzeżenia. Dodatkowo, na końcu funkcji można zdefiniować blok kodu finally, który jest zawsze wykonywany, niezależnie od tego, czy wystąpił błąd lub ostrzeżenie. Jest przydatny do zadań, które muszą zostać wykonane niezależnie od powodzenia działania programu (np. zamykanie połączenia do pliku czy bazy danych).

pierwiastek = function(x) {
  tryCatch(
    expr = {
      return(sqrt(x)) # testowana funkcja
    },
    error = function(e) {
      message("Funkcja zwróciła błąd!")
      return(NULL)
    },
    warning = function(w) {
      message("Funkcja zwróciła ostrzeżenie!")
      return(NULL)
    }
  )
}

Sprawdźmy teraz działanie funkcji pierwiastek() w zależności od rodzaju danych wejściowych.

pierwiastek(25)
[1] 5
pierwiastek(-10)
Funkcja zwróciła ostrzeżenie!
NULL
pierwiastek("tekst")
Funkcja zwróciła błąd!
NULL

Zadanie

  1. Napisz funkcję, która sprawdzi czy liczba jest parzysta. Jeśli warunek zostanie spełniony, to zwróci wartość logiczną TRUE, w przeciwnym razie FALSE.
  2. Napisz funkcję, która obliczy odległość euklidesową pomiędzy dwoma punktami.
  3. Napisz funkcję, która zamieni stopnie Celsjusza na Fahrenheita oraz wykona operację odwrotną. Rodzaj konwersji powinien zostać określony przez użytkownika.
  4. Dany jest wektor 1, 2, 3, NA, 5, 6, NA, 8, 9. Napisz funkcję, która zastąpi brakujące wartości (NA) średnią arytmetyczną lub wartością zdefiniowaną przez użytkownika. Załóżmy, że jeżeli użytkownik nie wskazał wartości (argument wejściowy domyślnie ustawiony jest jako pusta wartość NULL), to zostanie wykorzystana średnia. Do wykrycia brakujących wartości wykorzystaj funkcję is.na().