Abstrakcje oparte na funkcjach

W programowaniu funkcyjnym funkcje są obiektami pierwszej klasy (ang first class citizens) – oznacza to, że można je przypisywać do zmiennych, przekazywać jako argumenty do innych funkcji, a także zwracać jako wartości. Dzięki temu funkcje mogą pełnić rolę narzędzi do opisu operacji na danych w sposób modularny i abstrakcyjny.

Funkcje jako podstawowy element programu

Jedną z charakterystycznych cech stylu funkcyjnego jest stosowanie funkcji anonimowych, zwanych również funkcjami lambda. Umożliwiają one definiowanie krótkich, jednorazowych operacji:

1  double = lambda x: x * 2
2  print(double(5))  # wynik: 10
3
4  add = lambda a, b: a + b
5  print(add(3, 7))  # wynik: 10

Funkcje mogą być również przekazywane jako argumenty do innych funkcji. Przykład zastosowania funkcji map:

1  numbers = [1, 2, 3, 4]
2  squared = map(lambda x: x**2, numbers)
3  print(list(squared))  # wynik: [1, 4, 9, 16]

Takie podejście pozwala oddzielić logikę przetwarzania od danych, co zwiększa czytelność i reużywalność kodu.

Funkcje takie jak map, filter i reduce (z modułu functools) są typowymi przykładami narzędzi funkcyjnych dostępnych w Pythonie, które umożliwiają przekształcanie i filtrowanie zbiorów danych bez użycia pętli:

1  from functools import reduce
2
3  numbers = [1, 2, 3, 4]
4  total = reduce(lambda x, y: x + y, numbers)
5  print(total)  # wynik: 10

Funkcje wyższego rzędu

Funkcje wyższego rzędu to funkcje, które przyjmują inne funkcje jako argumenty lub zwracają funkcje jako wynik. Stanowią one istotny element programowania funkcyjnego, umożliwiając budowę elastycznego i abstrakcyjnego kodu. Dzięki nim można tworzyć funkcje o bardziej ogólnym przeznaczeniu i w prosty sposób łączyć różne operacje.

Typowym przykładem funkcji wyższego rzędu jest funkcja, która wykonuje daną operację na każdej wartości z listy:

1  def apply_to_each(fn, values):
2      return [fn(x) for x in values]
3
4  result = apply_to_each(lambda x: x * 10, [1, 2, 3])
5  print(result)  # wynik: [10, 20, 30]

Funkcje wyższego rzędu są także wykorzystywane do zwracania nowych funkcji – często wyspecjalizowanych wersji bardziej ogólnych operacji. Poniższy przykład tworzy funkcję zwracającą funkcję mnożącą przez stałą wartość:

1  def multiplier(factor):
2      def multiply(x):
3          return x * factor
4      return multiply
5
6  times3 = multiplier(3)
7  print(times3(5))  # wynik: 15

Dzięki temu możemy tworzyć nowe funkcje „w locie”, dopasowane do bieżącego kontekstu obliczeń. Takie podejście zwiększa elastyczność kodu i ułatwia jego ponowne wykorzystanie w różnych miejscach programu.