Koncepcje programowania funkcyjnego

Wprowadzenie do programowania funkcyjnego

Earth Engine korzysta z systemu przetwarzania równoległego, aby wykonywać obliczenia na dużej liczbie maszyn. Aby umożliwić takie przetwarzanie, Earth Engine korzysta ze standardowych technik powszechnie stosowanych w językach funkcyjnych, takich jak przejrzystość referencyjna i leniwe obliczanie, co zapewnia znaczną optymalizację i większą wydajność.

Główną koncepcją, która odróżnia programowanie funkcyjne od programowania proceduralnego, jest brak efektów ubocznych. Oznacza to, że napisane przez Ciebie funkcje nie korzystają z danych znajdujących się poza funkcją ani ich nie aktualizują. Jak zobaczysz w przykładach poniżej, można przekształcić problem tak, aby można go było rozwiązać za pomocą funkcji bez efektów ubocznych, które znacznie lepiej nadają się do wykonywania równoległego.

Pętle for

Używanie pętli for w Earth Engine jest odradzane. Te same wyniki można uzyskać za pomocą operacji map(), w której określisz funkcję, którą można niezależnie zastosować do każdego elementu. Dzięki temu system może rozdzielać przetwarzanie na różne maszyny.

Poniższy przykład pokazuje, jak na podstawie listy liczb utworzyć inną listę zawierającą kwadraty każdej liczby za pomocą funkcji map():

Edytor kodu (JavaScript)

// This generates a list of numbers from 1 to 10.
var myList = ee.List.sequence(1, 10);

// The map() operation takes a function that works on each element independently
// and returns a value. You define a function that can be applied to the input.
var computeSquares = function(number) {
  // We define the operation using the EE API.
  return ee.Number(number).pow(2);
};

// Apply your function to each item in the list by using the map() function.
var squares = myList.map(computeSquares);
print(squares);  // [1, 4, 9, 16, 25, 36, 49, 64, 81]

Warunki If/Else

Innym częstym problemem, z którym borykają się nowi użytkownicy przyzwyczajeni do paradygmatu programowania proceduralnego, jest prawidłowe używanie operatorów warunkowych if/else w Earth Engine. Interfejs API udostępnia ee.Algorithms.If() algorytm, ale jego używanie jest zdecydowanie odradzane. Zamiast niego zalecamy stosowanie bardziej funkcjonalnego podejścia z użyciem map() i filtrów. Earth Engine używa odroczonego wykonywania, co oznacza, że ocena wyrażenia jest opóźniona do momentu, w którym jego zrealizowana wartość jest rzeczywiście wymagana. W niektórych przypadkach ten typ modelu wykonania będzie oceniać zarówno prawdziwe, jak i fałszywe alternatywy instrukcji ee.Algorithms.If(). Może to prowadzić do dodatkowych obliczeń i wykorzystania pamięci w zależności od wyrażeń i zasobów wymaganych do ich wykonania.

Załóżmy, że chcesz rozwiązać wariant powyższego przykładu, w którym zadaniem jest obliczenie kwadratów tylko liczb nieparzystych. Funkcjonalne podejście do rozwiązania tego problemu bez warunków if/else przedstawiono poniżej:

Edytor kodu (JavaScript)

// The following function determines if a number is even or odd.  The mod(2)
// function returns 0 if the number is even and 1 if it is odd (the remainder
// after dividing by 2).  The input is multiplied by this remainder so even
// numbers get set to 0 and odd numbers are left unchanged.
var getOddNumbers = function(number) {
  number = ee.Number(number);   // Cast the input to a Number so we can use mod.
  var remainder = number.mod(2);
  return number.multiply(remainder);
};

var newList = myList.map(getOddNumbers);

// Remove the 0 values.
var oddNumbers = newList.removeAll([0]);

var squares = oddNumbers.map(computeSquares);
print(squares);  // [1, 9, 25, 49, 81]

Ten paradygmat ma szczególne zastosowanie w przypadku pracy z kolekcjami. Jeśli chcesz zastosować do kolekcji inny algorytm na podstawie pewnych warunków, najpierw odfiltruj kolekcję na podstawie warunku, a następnie zastosuj map() inną funkcję do każdej z podgrup. Dzięki temu system może wykonywać operację równolegle. Na przykład:

Edytor kodu (JavaScript)

// Import Landsat 8 TOA collection and filter to 2018 images.
var collection = ee.ImageCollection('LANDSAT/LC08/C02/T1_TOA')
  .filterDate('2018-01-01', '2019-01-01');

// Divide the collection into 2 subsets and apply a different algorithm on them.
var subset1 = collection.filter(ee.Filter.lt('SUN_ELEVATION', 40));
var subset2 = collection.filter(ee.Filter.gte('SUN_ELEVATION', 40));

// Multiply all images in subset1 collection by 2;
// do nothing to subset2 collection.
var processed1 = subset1.map(function(image) {
  return image.multiply(2);
});
var processed2 = subset2;

// Merge the collections to get a single collection.
var final = processed1.merge(processed2);
print('Original collection size', collection.size());
print('Processed collection size', final.size());

Łączna iteracja

Może być konieczne wykonanie operacji sekwencyjnej, w której wynik każdej iteracji jest używany w kolejnej iteracji. Earth Engine udostępnia do takich zadań metodę iterate(). Pamiętaj, że iterate() jest wykonywane sekwencyjnie, dlatego w przypadku dużych operacji będzie powolne. Używaj go tylko wtedy, gdy nie możesz uzyskać pożądanych wyników za pomocą elementu map() i filtrów.

Dobrym przykładem zastosowania funkcji iterate() jest tworzenie ciągu liczb Fibonacciego. W tym przypadku każda liczba w ciągu jest sumą 2 poprzednich liczb. Funkcja iterate() przyjmuje 2 argumenty: funkcję (algorytm) i wartość początkową. Funkcja otrzymuje 2 wartości: bieżącą wartość w iteracji i wynik poprzedniej iteracji. Poniższy przykład pokazuje, jak zaimplementować ciąg Fibonacciego w Earth Engine.

Edytor kodu (JavaScript)

var algorithm = function(current, previous) {
  previous = ee.List(previous);
  var n1 = ee.Number(previous.get(-1));
  var n2 = ee.Number(previous.get(-2));
  return previous.add(n1.add(n2));
};

// Compute 10 iterations.
var numIteration = ee.List.repeat(1, 10);
var start = [0, 1];
var sequence = numIteration.iterate(algorithm, start);
print(sequence);  // [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

Teraz, gdy dobrze rozumiesz koncepcje JavaScriptu, możesz zapoznać się z samouczkiem dotyczącym interfejsu API, aby poznać funkcje geoprzestrzenne interfejsu Earth Engine API.