Concepts de programmation fonctionnelle

Présentation de la programmation fonctionnelle

Earth Engine utilise un système de traitement parallèle pour effectuer des calculs sur un grand nombre de machines. Pour permettre un tel traitement, Earth Engine utilise des techniques standards couramment utilisées par les langages fonctionnels, telles que la transparence référentielle et l'évaluation différée, pour obtenir des gains d'optimisation et d'efficacité importants.

Le concept principal qui distingue la programmation fonctionnelle de la programmation procédurale est l'absence d'effets secondaires. Cela signifie que les fonctions que vous écrivez ne s'appuient pas sur des données en dehors de la fonction et ne les mettent pas à jour. Comme vous le verrez dans les exemples ci-dessous, il est possible de restructurer votre problème afin qu'il puisse être résolu à l'aide de fonctions sans effets secondaires, qui sont beaucoup mieux adaptées à l'exécution en parallèle.

Boucles For

L'utilisation de boucles "for" est déconseillée dans Earth Engine. Vous pouvez obtenir les mêmes résultats en utilisant une opération map() dans laquelle vous spécifiez une fonction qui peut être appliquée indépendamment à chaque élément. Cela permet au système de répartir le traitement sur différentes machines.

L'exemple ci-dessous montre comment créer une liste avec les carrés de chaque nombre à partir d'une liste de nombres à l'aide de map() :

Éditeur de code (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]

Conditions If/Else

Un autre problème courant rencontré par les nouveaux utilisateurs habitués au paradigme de programmation procédurale est l'utilisation appropriée des opérateurs conditionnels if/else dans Earth Engine. Bien que l'API fournisse un algorithme ee.Algorithms.If(), son utilisation est fortement déconseillée au profit d'une approche plus fonctionnelle utilisant map() et des filtres. Earth Engine utilise l' exécution différée, ce qui signifie que l'évaluation d'une expression est retardée jusqu'à ce que sa valeur réelle soit réellement requise. Dans certains cas, ce type de modèle d'exécution évalue les alternatives "true" et "false" d'une instruction ee.Algorithms.If(). Cela peut entraîner une utilisation supplémentaire du calcul et de la mémoire, en fonction des expressions et des ressources nécessaires à leur exécution.

Supposons que vous souhaitiez résoudre une variante de l'exemple ci-dessus, où la tâche consiste à calculer les carrés des nombres impairs uniquement. Une approche fonctionnelle pour résoudre ce problème sans conditions if/else est illustrée ci-dessous :

Éditeur de code (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]

Ce paradigme est particulièrement applicable lorsque vous travaillez avec des collections. Si vous souhaitez appliquer un algorithme différent à la collection en fonction de certaines conditions, la méthode recommandée consiste à filtrer d'abord la collection en fonction de la condition, puis à map() une fonction différente à chacun des sous-ensembles. Cela permet au système de paralléliser l'opération. Par exemple :

Éditeur de code (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());

Itération cumulative

Vous devrez peut-être effectuer une opération séquentielle, où le résultat de chaque itération est utilisé par l'itération suivante. Earth Engine fournit une méthode iterate() pour ces tâches. N'oubliez pas que iterate() est exécuté de manière séquentielle et sera donc lent pour les opérations de grande envergure. Utilisez-le uniquement lorsque vous ne pouvez pas utiliser map() et des filtres pour obtenir le résultat souhaité.

Une bonne démonstration de iterate() consiste à créer une séquence de nombres de Fibonacci. Ici, chaque nombre de la série est la somme des deux nombres précédents. La fonction iterate() prend deux arguments : une fonction (algorithme) et une valeur de départ. La fonction elle-même reçoit deux valeurs : la valeur actuelle de l'itération et le résultat de l'itération précédente. L'exemple suivant montre comment implémenter une séquence de Fibonacci dans Earth Engine.

Éditeur de code (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]

Maintenant que vous avez une bonne compréhension des concepts JavaScript, vous pouvez consulter le tutoriel sur l'API pour découvrir les fonctionnalités géospatiales de l'API Earth Engine.