Konzepte der funktionalen Programmierung

Einführung in die funktionale Programmierung

Earth Engine verwendet ein System für die parallele Verarbeitung, um Berechnungen auf einer großen Anzahl von Maschinen durchzuführen. Um eine solche Verarbeitung zu ermöglichen, nutzt Earth Engine Standardtechniken, die häufig von funktionalen Sprachen verwendet werden, z. B. referenzielle Transparenz und Lazy Evaluation, um die Optimierung und Effizienz deutlich zu steigern.

Das Hauptkonzept, das die funktionale Programmierung von der prozeduralen Programmierung unterscheidet, ist das Fehlen von Nebeneffekten. Das bedeutet, dass die von Ihnen geschriebenen Funktionen nicht auf Daten außerhalb der Funktion angewiesen sind und diese Daten auch nicht aktualisieren. Wie Sie in den Beispielen unten sehen, ist es möglich, Ihr Problem so umzustrukturieren, dass es mit Funktionen ohne Nebeneffekte gelöst werden kann, die sich viel besser für die parallele Ausführung eignen.

For-Schleifen

Die Verwendung von For-Schleifen wird in Earth Engine nicht empfohlen. Die gleichen Ergebnisse können mit einem map()-Vorgang erzielt werden, bei dem Sie eine Funktion angeben, die unabhängig auf jedes Element angewendet werden kann. So kann das System die Verarbeitung auf verschiedene Computer verteilen.

Im folgenden Beispiel wird veranschaulicht, wie Sie mit map() aus einer Liste von Zahlen eine weitere Liste mit den Quadraten der einzelnen Zahlen erstellen:

Code-Editor (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]

If/Else-Bedingungen

Ein weiteres häufiges Problem für neue Nutzer, die an das prozedurale Programmierparadigma gewöhnt sind, ist die richtige Verwendung von if/else-Bedingungsoperatoren in Earth Engine. Die API bietet zwar einen ee.Algorithms.If()-Algorithmus, die Verwendung wird jedoch dringend zugunsten eines funktionaleren Ansatzes mit map() und Filtern abgeraten. In Earth Engine wird die verzögerte Ausführung verwendet. Das bedeutet, dass die Auswertung eines Ausdrucks verzögert wird, bis der tatsächliche Wert benötigt wird. In einigen Fällen werden bei diesem Ausführungsmodell sowohl die „true“- als auch die „false“-Alternativen einer ee.Algorithms.If()-Anweisung ausgewertet. Dies kann je nach den Ausdrücken und den für ihre Ausführung erforderlichen Ressourcen zu zusätzlichem Rechenaufwand und zusätzlicher Speichernutzung führen.

Angenommen, Sie möchten eine Variante des obigen Beispiels lösen, bei der die Aufgabe darin besteht, nur die Quadrate ungerader Zahlen zu berechnen. Ein funktionaler Ansatz zur Lösung dieses Problems ohne if/else-Bedingungen wird unten gezeigt:

Code-Editor (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]

Dieses Paradigma ist besonders nützlich, wenn Sie mit Sammlungen arbeiten. Wenn Sie basierend auf bestimmten Bedingungen einen anderen Algorithmus auf die Sammlung anwenden möchten, filtern Sie die Sammlung am besten zuerst nach der Bedingung und wenden dann map() auf jede der Teilmengen an. So kann das System den Vorgang parallelisieren. Beispiel:

Code-Editor (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());

Kumulative Iteration

Möglicherweise müssen Sie sequenzielle Vorgänge ausführen, bei denen das Ergebnis jeder Iteration von der nachfolgenden Iteration verwendet wird. Earth Engine bietet für solche Aufgaben die Methode iterate(). Denken Sie daran, dass iterate() sequenziell ausgeführt wird und daher bei großen Vorgängen langsam ist. Verwenden Sie diese Funktion nur, wenn Sie mit map() und Filtern nicht die gewünschte Ausgabe erzielen können.

Ein gutes Beispiel für iterate() ist die Erstellung einer Fibonacci-Zahlenfolge. Hier ist jede Zahl in der Reihe die Summe der beiden vorherigen Zahlen. Die Funktion iterate() verwendet zwei Argumente: eine Funktion (Algorithmus) und einen Startwert. Der Funktion selbst werden zwei Werte übergeben: der aktuelle Wert in der Iteration und das Ergebnis der vorherigen Iteration. Das folgende Beispiel zeigt, wie eine Fibonacci-Folge in Earth Engine implementiert wird.

Code-Editor (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]

Da Sie nun mit den JavaScript-Konzepten vertraut sind, können Sie sich im API-Tutorial über die raumbezogenen Funktionen der Earth Engine API informieren.