Читать книгу Programowanie funkcyjne w języku C#. Jak pisać lepszy kod - Enrico Buonanno - Страница 15
1.1.2 Unikanie zmiany stanu
ОглавлениеJeśli przestrzegamy paradygmatu funkcyjnego, powinniśmy całkowicie powstrzymać się od zmieniania stanu: po utworzeniu obiekt nigdy się nie zmienia, a zmienne nie powinny mieć nadawanych nowych wartości. Określenie zmiana wskazuje, że wartość zmieniana jest w danym miejscu – poprzez aktualizację wartości przechowywanej gdzieś w pamięci. Na przykład poniższy kod tworzy i wypełnia wartościami tablicę, a następnie aktualizuje jedną z wartości tablicy w miejscu:
int[] nums = { 1, 2, 3 };
nums[0] = 7;
nums // => [7, 2, 3]
Takie aktualizacje są też nazywane destrukcyjnymi, gdyż wartość wcześniej zapisana jest niszczona podczas aktualizacji. Przy kodowaniu funkcyjnym trzeba ich zawsze unikać. (Czysto funkcyjne języki w ogóle nie pozwalają na zmianę wartości w miejscu). Według tej zasady sortowanie lub filtrowanie listy nie powinno zmieniać listy w miejscu jej położenia, ale musi tworzyć nową, odpowiednio przefiltrowaną lub posortowaną listę, bez zmian w oryginale.
Wpiszmy poniższe instrukcje do REPL, aby zobaczyć, co się stanie podczas sortowania lub filtrowania listy za pomocą funkcji Where i OrderBy z LINQ.
Func<int, bool> isOdd = x => x % 2 == 1;
int[] original = { 7, 6, 1 };
Oryginalna lista pozostała bez zmian
var sorted = original.OrderBy(x => x);
var filtered = original.Where(isOdd);
original // => [7, 6, 1]
Sortowanie i filtrowanie tworzą nowe listy
sorted // => [1, 6, 7]
filtered // => [7, 1]
Listing 1.1 Podejście funkcyjne: Where i OrderBy nie wpływają na oryginalną listę
Jak możemy zobaczyć, oryginalna lista pozostaje niezmieniona przez działania filtrowania lub sortowania, które utworzyły nowe zmienne typu IEnumerable.
Popatrzmy na przeciwny przykład. Jeśli mamy List<T>, możemy posortować ją w miejscu, wywołując jej metodę Sort.
var original = new List<int> { 5, 7, 1 };
original.Sort();
original // => [1, 5, 7]
Listing 1.2 Podejście niefunkcyjne: List<T>.Sort sortuje listę w miejscu
W tym przypadku po posortowaniu oryginalne uporządkowanie zostaje zniszczone. Zaraz zobaczymy, dlaczego jest to problematyczne.
UWAGA Powód, dla którego pokazujemy podejście funkcyjne i niefunkcyjne, jest historyczny: List<T>.Sort poprzedza LINQ, które spowodowało decydujący zwrot w kierunku programowania funkcyjnego.